@numerum-tech/yeriasdk 1.0.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.
- package/README.md +864 -0
- package/dist/core/action-grid-view.d.ts +57 -0
- package/dist/core/action-grid-view.d.ts.map +1 -0
- package/dist/core/action-grid-view.js +112 -0
- package/dist/core/action-grid-view.js.map +1 -0
- package/dist/core/action-list-view.d.ts +39 -0
- package/dist/core/action-list-view.d.ts.map +1 -0
- package/dist/core/action-list-view.js +77 -0
- package/dist/core/action-list-view.js.map +1 -0
- package/dist/core/base/base-action-view.d.ts +82 -0
- package/dist/core/base/base-action-view.d.ts.map +1 -0
- package/dist/core/base/base-action-view.js +143 -0
- package/dist/core/base/base-action-view.js.map +1 -0
- package/dist/core/base-view.d.ts +105 -0
- package/dist/core/base-view.d.ts.map +1 -0
- package/dist/core/base-view.js +426 -0
- package/dist/core/base-view.js.map +1 -0
- package/dist/core/card-view.d.ts +24 -0
- package/dist/core/card-view.d.ts.map +1 -0
- package/dist/core/card-view.js +127 -0
- package/dist/core/card-view.js.map +1 -0
- package/dist/core/carousel-view.d.ts +22 -0
- package/dist/core/carousel-view.d.ts.map +1 -0
- package/dist/core/carousel-view.js +99 -0
- package/dist/core/carousel-view.js.map +1 -0
- package/dist/core/form-view.d.ts +165 -0
- package/dist/core/form-view.d.ts.map +1 -0
- package/dist/core/form-view.js +365 -0
- package/dist/core/form-view.js.map +1 -0
- package/dist/core/jsonapp.d.ts +263 -0
- package/dist/core/jsonapp.d.ts.map +1 -0
- package/dist/core/jsonapp.js +528 -0
- package/dist/core/jsonapp.js.map +1 -0
- package/dist/core/key-store.d.ts +51 -0
- package/dist/core/key-store.d.ts.map +1 -0
- package/dist/core/key-store.js +138 -0
- package/dist/core/key-store.js.map +1 -0
- package/dist/core/map-view.d.ts +45 -0
- package/dist/core/map-view.d.ts.map +1 -0
- package/dist/core/map-view.js +318 -0
- package/dist/core/map-view.js.map +1 -0
- package/dist/core/media-view.d.ts +20 -0
- package/dist/core/media-view.d.ts.map +1 -0
- package/dist/core/media-view.js +75 -0
- package/dist/core/media-view.js.map +1 -0
- package/dist/core/message-view.d.ts +53 -0
- package/dist/core/message-view.d.ts.map +1 -0
- package/dist/core/message-view.js +109 -0
- package/dist/core/message-view.js.map +1 -0
- package/dist/core/notification.d.ts +17 -0
- package/dist/core/notification.d.ts.map +1 -0
- package/dist/core/notification.js +33 -0
- package/dist/core/notification.js.map +1 -0
- package/dist/core/qr-display-view.d.ts +32 -0
- package/dist/core/qr-display-view.d.ts.map +1 -0
- package/dist/core/qr-display-view.js +66 -0
- package/dist/core/qr-display-view.js.map +1 -0
- package/dist/core/qr-scan-view.d.ts +148 -0
- package/dist/core/qr-scan-view.d.ts.map +1 -0
- package/dist/core/qr-scan-view.js +259 -0
- package/dist/core/qr-scan-view.js.map +1 -0
- package/dist/core/reader-view.d.ts +73 -0
- package/dist/core/reader-view.d.ts.map +1 -0
- package/dist/core/reader-view.js +285 -0
- package/dist/core/reader-view.js.map +1 -0
- package/dist/core/timeline-view.d.ts +16 -0
- package/dist/core/timeline-view.d.ts.map +1 -0
- package/dist/core/timeline-view.js +68 -0
- package/dist/core/timeline-view.js.map +1 -0
- package/dist/errors/index.d.ts +276 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +431 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +103 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +576 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +48 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/fileFormats.d.ts +52 -0
- package/dist/utils/fileFormats.d.ts.map +1 -0
- package/dist/utils/fileFormats.js +198 -0
- package/dist/utils/fileFormats.js.map +1 -0
- package/dist/utils/logger.d.ts +38 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +109 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/validators.d.ts +48 -0
- package/dist/utils/validators.d.ts.map +1 -0
- package/dist/utils/validators.js +445 -0
- package/dist/utils/validators.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JsonApp = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
const form_view_1 = require("./form-view");
|
|
6
|
+
const reader_view_1 = require("./reader-view");
|
|
7
|
+
const action_list_view_1 = require("./action-list-view");
|
|
8
|
+
const action_grid_view_1 = require("./action-grid-view");
|
|
9
|
+
const qr_scan_view_1 = require("./qr-scan-view");
|
|
10
|
+
const qr_display_view_1 = require("./qr-display-view");
|
|
11
|
+
const message_view_1 = require("./message-view");
|
|
12
|
+
const card_view_1 = require("./card-view");
|
|
13
|
+
const carousel_view_1 = require("./carousel-view");
|
|
14
|
+
const timeline_view_1 = require("./timeline-view");
|
|
15
|
+
const media_view_1 = require("./media-view");
|
|
16
|
+
const map_view_1 = require("./map-view");
|
|
17
|
+
const errors_1 = require("../errors");
|
|
18
|
+
const notification_1 = require("./notification");
|
|
19
|
+
class JsonApp {
|
|
20
|
+
constructor(config) {
|
|
21
|
+
this.config = {
|
|
22
|
+
allowedDomains: [],
|
|
23
|
+
viewExpirationMinutes: 60,
|
|
24
|
+
...config
|
|
25
|
+
};
|
|
26
|
+
// Génération ou utilisation des clés Ed25519
|
|
27
|
+
if (config.privateKey && config.publicKey) {
|
|
28
|
+
this.privateKey = config.privateKey;
|
|
29
|
+
this.publicKey = config.publicKey;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// Génération automatique des clés Ed25519
|
|
33
|
+
const keyPair = (0, crypto_1.generateKeyPairSync)('ed25519');
|
|
34
|
+
this.privateKey = keyPair.privateKey.export({ type: 'pkcs8', format: 'pem' });
|
|
35
|
+
this.publicKey = keyPair.publicKey.export({ type: 'spki', format: 'pem' });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Crée une vue de formulaire sécurisée
|
|
40
|
+
*/
|
|
41
|
+
createFormView(formId, title, processId) {
|
|
42
|
+
return new form_view_1.FormView(formId, title, processId);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Crée une vue de lecture sécurisée
|
|
46
|
+
*/
|
|
47
|
+
createReaderView(viewId, title, processId) {
|
|
48
|
+
return new reader_view_1.ReaderView(viewId, title, processId);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Crée une vue de liste d'actions sécurisée
|
|
52
|
+
*/
|
|
53
|
+
createActionListView(viewId, title, processId) {
|
|
54
|
+
return new action_list_view_1.ActionListView(viewId, title, processId);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Crée une vue de grille d'actions sécurisée
|
|
58
|
+
*/
|
|
59
|
+
createActionGridView(viewId, title, processId) {
|
|
60
|
+
return new action_grid_view_1.ActionGridView(viewId, title, processId);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Crée une vue de scan QR sécurisée
|
|
64
|
+
*/
|
|
65
|
+
createQRScanView(viewId, title, processId) {
|
|
66
|
+
return new qr_scan_view_1.QRScanView(viewId, title, processId);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Crée une vue d'affichage QR sécurisée
|
|
70
|
+
*/
|
|
71
|
+
createQRDisplayView(viewId, title, processId) {
|
|
72
|
+
return new qr_display_view_1.QRDisplayView(viewId, title, processId);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Crée une vue de message sécurisée
|
|
76
|
+
*/
|
|
77
|
+
createMessageView(viewId, title, processId) {
|
|
78
|
+
return new message_view_1.MessageView(viewId, title, processId);
|
|
79
|
+
}
|
|
80
|
+
createCardView(viewId, title, processId) {
|
|
81
|
+
return new card_view_1.CardView(viewId, title, processId);
|
|
82
|
+
}
|
|
83
|
+
createCarouselView(viewId, title, processId) {
|
|
84
|
+
return new carousel_view_1.CarouselView(viewId, title, processId);
|
|
85
|
+
}
|
|
86
|
+
createTimelineView(viewId, title, processId) {
|
|
87
|
+
return new timeline_view_1.TimelineView(viewId, title, processId);
|
|
88
|
+
}
|
|
89
|
+
createMediaView(viewId, title, processId) {
|
|
90
|
+
return new media_view_1.MediaView(viewId, title, processId);
|
|
91
|
+
}
|
|
92
|
+
createMapView(viewId, title, processId) {
|
|
93
|
+
return new map_view_1.MapView(viewId, title, processId);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Crée une notification pour un utilisateur spécifique
|
|
97
|
+
*/
|
|
98
|
+
createNotification(userId, title, body, link) {
|
|
99
|
+
return new notification_1.Notification(userId, title, body, link);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Signe une notification avec Ed25519
|
|
103
|
+
*/
|
|
104
|
+
signNotification(notification) {
|
|
105
|
+
const notificationJson = notification.toJSON();
|
|
106
|
+
const timestamp = Date.now();
|
|
107
|
+
// Payload must match backend reconstruction: { notification (object), timestamp, appId }
|
|
108
|
+
const payload = JSON.stringify({
|
|
109
|
+
notification: notificationJson,
|
|
110
|
+
timestamp: timestamp,
|
|
111
|
+
appId: this.config.appId
|
|
112
|
+
});
|
|
113
|
+
const signatureBuffer = (0, crypto_1.sign)(null, Buffer.from(payload), this.privateKey);
|
|
114
|
+
const signature = signatureBuffer.toString('base64');
|
|
115
|
+
return {
|
|
116
|
+
appId: this.config.appId,
|
|
117
|
+
signature,
|
|
118
|
+
timestamp,
|
|
119
|
+
notification: notificationJson
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Envoie une notification signée à la plateforme City-Mate
|
|
124
|
+
* @param notification - Notification à envoyer
|
|
125
|
+
* @param platformUrl - URL de la plateforme (optionnel, utilise config.platformUrl si non fourni)
|
|
126
|
+
* @throws {ConfigurationError} Si aucune URL de plateforme n'est configurée
|
|
127
|
+
* @throws {ExternalError} Si l'envoi échoue
|
|
128
|
+
*/
|
|
129
|
+
async sendNotification(notification, platformUrl) {
|
|
130
|
+
const signedNotification = this.signNotification(notification);
|
|
131
|
+
const url = platformUrl || this.config.platformUrl;
|
|
132
|
+
if (!url) {
|
|
133
|
+
throw new errors_1.ConfigurationError('Platform URL required for sending notifications. Set platformUrl in JsonAppConfig or pass it as parameter.');
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
const response = await fetch(url, {
|
|
137
|
+
method: 'POST',
|
|
138
|
+
headers: { 'Content-Type': 'application/json' },
|
|
139
|
+
body: JSON.stringify(signedNotification),
|
|
140
|
+
signal: AbortSignal.timeout(this.config.notificationTimeout || 5000)
|
|
141
|
+
});
|
|
142
|
+
if (!response.ok) {
|
|
143
|
+
throw new errors_1.ExternalError(errors_1.ERROR_CODES.NOTIFICATION_FAILED, `Failed to send notification: ${response.status} ${response.statusText}`, 'fetch');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
if (error instanceof errors_1.ExternalError || error instanceof errors_1.ConfigurationError) {
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
throw new errors_1.ExternalError(errors_1.ERROR_CODES.NOTIFICATION_FAILED, `Failed to send notification: ${error instanceof Error ? error.message : String(error)}`, 'fetch', error instanceof Error ? error : undefined);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Rotate the service's signing key on the Yeria registry. The rotation
|
|
155
|
+
* envelope is signed with the *current* private key — the caller proves
|
|
156
|
+
* they own the still-valid keypair before the new one is accepted.
|
|
157
|
+
*
|
|
158
|
+
* After this call succeeds:
|
|
159
|
+
* - The new public key becomes Active on Yeria.
|
|
160
|
+
* - The old key keeps validating signatures for ~5 minutes (grace
|
|
161
|
+
* window so in-flight signed responses still verify).
|
|
162
|
+
* - This JsonApp instance is mutated to use the new keypair locally.
|
|
163
|
+
*
|
|
164
|
+
* @param yeriaApiBaseUrl - Yeria registry root, e.g. `https://yeria.app`
|
|
165
|
+
* @param serviceId - The service id assigned by Yeria
|
|
166
|
+
* @param newKeys - The freshly-generated keypair to switch to
|
|
167
|
+
*/
|
|
168
|
+
async rotateKey(yeriaApiBaseUrl, serviceId, newKeys) {
|
|
169
|
+
if (!newKeys?.privateKey || !newKeys?.publicKey) {
|
|
170
|
+
throw new errors_1.ConfigurationError('rotateKey requires { privateKey, publicKey } in PEM');
|
|
171
|
+
}
|
|
172
|
+
const envelope = {
|
|
173
|
+
serviceId: String(serviceId),
|
|
174
|
+
newPublicKey: newKeys.publicKey,
|
|
175
|
+
timestamp: Date.now()
|
|
176
|
+
};
|
|
177
|
+
const payload = JSON.stringify(envelope);
|
|
178
|
+
const signature = (0, crypto_1.sign)(null, Buffer.from(payload), this.privateKey).toString('base64');
|
|
179
|
+
const url = `${yeriaApiBaseUrl.replace(/\/+$/, '')}/api/v1/services/${serviceId}/keys/rotate`;
|
|
180
|
+
let response;
|
|
181
|
+
try {
|
|
182
|
+
response = await fetch(url, {
|
|
183
|
+
method: 'POST',
|
|
184
|
+
headers: { 'Content-Type': 'application/json' },
|
|
185
|
+
body: JSON.stringify({
|
|
186
|
+
envelope,
|
|
187
|
+
signature,
|
|
188
|
+
currentPublicKey: this.publicKey
|
|
189
|
+
}),
|
|
190
|
+
signal: AbortSignal.timeout(this.config.notificationTimeout || 5000)
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
throw new errors_1.ExternalError(errors_1.ERROR_CODES.NOTIFICATION_FAILED, `Key rotation request failed: ${error instanceof Error ? error.message : String(error)}`, 'fetch', error instanceof Error ? error : undefined);
|
|
195
|
+
}
|
|
196
|
+
if (!response.ok) {
|
|
197
|
+
const text = await response.text().catch(() => '');
|
|
198
|
+
throw new errors_1.ExternalError(errors_1.ERROR_CODES.NOTIFICATION_FAILED, `Key rotation rejected: ${response.status} ${response.statusText} ${text}`, 'fetch');
|
|
199
|
+
}
|
|
200
|
+
const body = await response.json();
|
|
201
|
+
// Locally swap to the new keypair so subsequent serve() calls sign
|
|
202
|
+
// with it. Old responses still verify against the old public key
|
|
203
|
+
// during the grace window because Yeria keeps both rows Active.
|
|
204
|
+
this.privateKey = newKeys.privateKey;
|
|
205
|
+
this.publicKey = newKeys.publicKey;
|
|
206
|
+
return {
|
|
207
|
+
keyId: body.data?.key?.id,
|
|
208
|
+
expiresAt: body.data?.key?.expires_at,
|
|
209
|
+
gracePeriodMinutes: body.data?.grace_period_minutes ?? 5
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Sérialise une vue, signe les bytes sérialisés, et retourne une
|
|
214
|
+
* enveloppe `{payload, signature}` à envoyer telle quelle au client.
|
|
215
|
+
*
|
|
216
|
+
* Usage côté provider (Express) :
|
|
217
|
+
* res.json(jsonApp.serve(view));
|
|
218
|
+
* res.status(400).json(jsonApp.serve(errorMessage));
|
|
219
|
+
*
|
|
220
|
+
* La signature est dans la structure de retour — impossible de
|
|
221
|
+
* l'oublier en l'envoyant. Le client vérifie sur `payload` (string)
|
|
222
|
+
* sans re-stringification.
|
|
223
|
+
*/
|
|
224
|
+
serve(view) {
|
|
225
|
+
const decoded = {
|
|
226
|
+
appId: this.config.appId,
|
|
227
|
+
timestamp: Date.now(),
|
|
228
|
+
view: view.toJSON()
|
|
229
|
+
};
|
|
230
|
+
const payload = JSON.stringify(decoded);
|
|
231
|
+
const signature = (0, crypto_1.sign)(null, Buffer.from(payload, 'utf8'), this.privateKey).toString('base64');
|
|
232
|
+
return { payload, signature };
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Obtient la clé publique pour la vérification côté frontend
|
|
236
|
+
*/
|
|
237
|
+
getPublicKey() {
|
|
238
|
+
return this.publicKey;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Vérifie l'intégrité d'une enveloppe signée reçue du wire.
|
|
242
|
+
*
|
|
243
|
+
* @throws {AppIdMismatchError} si appId ne matche pas this.config.appId
|
|
244
|
+
* @throws {ViewExpiredError} si timestamp > viewExpirationMinutes
|
|
245
|
+
* @throws {SignatureVerificationError} si signature invalide ou payload malformé
|
|
246
|
+
*/
|
|
247
|
+
verifyIntegrity(envelope) {
|
|
248
|
+
try {
|
|
249
|
+
// 1. Verify signature on payload bytes — no re-stringification
|
|
250
|
+
const signatureBuffer = Buffer.from(envelope.signature, 'base64');
|
|
251
|
+
const isValid = (0, crypto_1.verify)(null, Buffer.from(envelope.payload, 'utf8'), this.publicKey, signatureBuffer);
|
|
252
|
+
if (!isValid) {
|
|
253
|
+
throw new errors_1.SignatureVerificationError(this.config.appId);
|
|
254
|
+
}
|
|
255
|
+
// 2. Parse the now-trusted payload
|
|
256
|
+
const decoded = JSON.parse(envelope.payload);
|
|
257
|
+
// 3. appId match
|
|
258
|
+
if (decoded.appId !== this.config.appId) {
|
|
259
|
+
throw new errors_1.AppIdMismatchError(this.config.appId, decoded.appId);
|
|
260
|
+
}
|
|
261
|
+
// 4. Expiration window
|
|
262
|
+
const now = Date.now();
|
|
263
|
+
const expirationTime = this.config.viewExpirationMinutes * 60 * 1000;
|
|
264
|
+
const age = now - decoded.timestamp;
|
|
265
|
+
if (age > expirationTime) {
|
|
266
|
+
const viewId = typeof decoded.view['id'] === 'string'
|
|
267
|
+
? decoded.view['id']
|
|
268
|
+
: 'unknown';
|
|
269
|
+
throw new errors_1.ViewExpiredError(viewId, age, expirationTime);
|
|
270
|
+
}
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
if (error instanceof errors_1.AppIdMismatchError ||
|
|
275
|
+
error instanceof errors_1.ViewExpiredError ||
|
|
276
|
+
error instanceof errors_1.SignatureVerificationError) {
|
|
277
|
+
throw error;
|
|
278
|
+
}
|
|
279
|
+
throw new errors_1.SignatureVerificationError(this.config.appId);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Méthode statique pour vérifier une signature.
|
|
284
|
+
* @param publicKey - Clé publique Ed25519 (PEM format)
|
|
285
|
+
* @param payload - JSON string signée (extraite de l'enveloppe)
|
|
286
|
+
* @param signature - Signature base64
|
|
287
|
+
* @param onError - Optional error callback for logging
|
|
288
|
+
* @returns true si la signature est valide
|
|
289
|
+
*/
|
|
290
|
+
static verifySignature(publicKey, payload, signature, onError) {
|
|
291
|
+
try {
|
|
292
|
+
const signatureBuffer = Buffer.from(signature, 'base64');
|
|
293
|
+
return (0, crypto_1.verify)(null, Buffer.from(payload, 'utf8'), publicKey, signatureBuffer);
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
const err = error instanceof Error ? error : new Error('Unknown error');
|
|
297
|
+
if (onError) {
|
|
298
|
+
onError(err);
|
|
299
|
+
}
|
|
300
|
+
else if (typeof process !== 'undefined' && process.env?.['NODE_ENV'] !== 'production') {
|
|
301
|
+
console.error('[JsonApp] Error verifying signature:', err);
|
|
302
|
+
}
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Génère statiquement une enveloppe signée sans instance JsonApp.
|
|
308
|
+
*/
|
|
309
|
+
static signView(view, appId, privateKey, timestamp = Date.now()) {
|
|
310
|
+
const decoded = { appId, timestamp, view };
|
|
311
|
+
const payload = JSON.stringify(decoded);
|
|
312
|
+
const signature = (0, crypto_1.sign)(null, Buffer.from(payload, 'utf8'), privateKey).toString('base64');
|
|
313
|
+
return { payload, signature };
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Verify a Yeria-issued user token (RS256 JWT signed by Yeria's
|
|
317
|
+
* Registry RSA key). Used by providers to authenticate inbound
|
|
318
|
+
* requests carrying `Authorization: Bearer <serviceToken>`.
|
|
319
|
+
*
|
|
320
|
+
* @param jwtToken Compact JWT (header.payload.signature, base64url).
|
|
321
|
+
* @param yeriaPublicKey Yeria's Registry public key in PEM format.
|
|
322
|
+
* Fetch once at boot from
|
|
323
|
+
* `GET /api/v1/registry/public-key` and cache.
|
|
324
|
+
* @param expectedServiceId When supplied, asserts `aud === String(expectedServiceId)`.
|
|
325
|
+
* Pass the service id your backend is hosting; refuse
|
|
326
|
+
* tokens scoped to a different service.
|
|
327
|
+
* @returns The verified claims object.
|
|
328
|
+
* @throws SignatureVerificationError if signature invalid / token malformed / wrong issuer / wrong audience.
|
|
329
|
+
* @throws ViewExpiredError if the `exp` claim is in the past.
|
|
330
|
+
*/
|
|
331
|
+
static verifyUserToken(jwtToken, yeriaPublicKey, expectedServiceId) {
|
|
332
|
+
if (typeof jwtToken !== 'string' || jwtToken.length === 0) {
|
|
333
|
+
throw new errors_1.SignatureVerificationError('yeria', 'missing jwt');
|
|
334
|
+
}
|
|
335
|
+
const parts = jwtToken.split('.');
|
|
336
|
+
if (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2]) {
|
|
337
|
+
throw new errors_1.SignatureVerificationError('yeria', 'malformed jwt');
|
|
338
|
+
}
|
|
339
|
+
const headerB64 = parts[0];
|
|
340
|
+
const payloadB64 = parts[1];
|
|
341
|
+
const signatureB64 = parts[2];
|
|
342
|
+
let header;
|
|
343
|
+
let claims;
|
|
344
|
+
try {
|
|
345
|
+
header = JSON.parse(base64UrlDecodeToString(headerB64));
|
|
346
|
+
claims = JSON.parse(base64UrlDecodeToString(payloadB64));
|
|
347
|
+
}
|
|
348
|
+
catch (_e) {
|
|
349
|
+
throw new errors_1.SignatureVerificationError('yeria', 'invalid base64 / json');
|
|
350
|
+
}
|
|
351
|
+
if (header['alg'] !== 'RS256') {
|
|
352
|
+
throw new errors_1.SignatureVerificationError('yeria', `unsupported alg: ${String(header['alg'])}`);
|
|
353
|
+
}
|
|
354
|
+
// Verify RSA-SHA256 over the canonical signing input.
|
|
355
|
+
const signingInput = `${headerB64}.${payloadB64}`;
|
|
356
|
+
const signatureBytes = base64UrlDecodeToBuffer(signatureB64);
|
|
357
|
+
const verifier = (0, crypto_1.createVerify)('RSA-SHA256');
|
|
358
|
+
verifier.update(signingInput);
|
|
359
|
+
verifier.end();
|
|
360
|
+
const sigOk = verifier.verify(yeriaPublicKey, signatureBytes);
|
|
361
|
+
if (!sigOk) {
|
|
362
|
+
throw new errors_1.SignatureVerificationError('yeria', 'signature mismatch');
|
|
363
|
+
}
|
|
364
|
+
if (claims.iss !== 'yeria') {
|
|
365
|
+
throw new errors_1.SignatureVerificationError('yeria', `unexpected issuer: ${String(claims.iss)}`);
|
|
366
|
+
}
|
|
367
|
+
if (expectedServiceId !== undefined && claims.aud !== String(expectedServiceId)) {
|
|
368
|
+
throw new errors_1.SignatureVerificationError('yeria', `audience mismatch: token aud=${claims.aud}, expected=${String(expectedServiceId)}`);
|
|
369
|
+
}
|
|
370
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
371
|
+
if (typeof claims.exp !== 'number' || claims.exp <= nowSec) {
|
|
372
|
+
throw new errors_1.ViewExpiredError('user-token', (nowSec - (claims.exp || 0)) * 1000, 0);
|
|
373
|
+
}
|
|
374
|
+
// Surface kid from the header for caller convenience.
|
|
375
|
+
if (typeof header['kid'] === 'string') {
|
|
376
|
+
claims.kid = header['kid'];
|
|
377
|
+
}
|
|
378
|
+
return claims;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Same audience-scoped JWT verify as `verifyUserToken`, but the second
|
|
382
|
+
* argument is a kid resolver instead of a fixed PEM. The SDK pulls
|
|
383
|
+
* the `kid` from the JWT header, calls `resolver(kid)`, and verifies
|
|
384
|
+
* the signature against whatever PEM comes back.
|
|
385
|
+
*
|
|
386
|
+
* Pair with `YeriaKeyStore` so providers don't have to write key-
|
|
387
|
+
* fetching, caching or rotation-grace handling themselves.
|
|
388
|
+
*
|
|
389
|
+
* @throws SignatureVerificationError if the resolver returns null, the
|
|
390
|
+
* signature fails, the issuer / audience mismatches, or the
|
|
391
|
+
* JWT is malformed.
|
|
392
|
+
* @throws ViewExpiredError when `exp` is in the past.
|
|
393
|
+
*/
|
|
394
|
+
static async verifyUserTokenWithResolver(jwtToken, resolver, expectedServiceId) {
|
|
395
|
+
if (typeof jwtToken !== 'string' || jwtToken.length === 0) {
|
|
396
|
+
throw new errors_1.SignatureVerificationError('yeria', 'missing jwt');
|
|
397
|
+
}
|
|
398
|
+
const parts = jwtToken.split('.');
|
|
399
|
+
if (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2]) {
|
|
400
|
+
throw new errors_1.SignatureVerificationError('yeria', 'malformed jwt');
|
|
401
|
+
}
|
|
402
|
+
let header;
|
|
403
|
+
try {
|
|
404
|
+
header = JSON.parse(base64UrlDecodeToString(parts[0]));
|
|
405
|
+
}
|
|
406
|
+
catch (_e) {
|
|
407
|
+
throw new errors_1.SignatureVerificationError('yeria', 'invalid header');
|
|
408
|
+
}
|
|
409
|
+
const kid = header['kid'];
|
|
410
|
+
if (typeof kid !== 'string' || kid.length === 0) {
|
|
411
|
+
throw new errors_1.SignatureVerificationError('yeria', 'jwt header missing kid');
|
|
412
|
+
}
|
|
413
|
+
const pem = await resolver(kid);
|
|
414
|
+
if (!pem) {
|
|
415
|
+
// Resolver returns null when Yeria says the key is expired or
|
|
416
|
+
// the kid is unknown. Either way the token cannot be trusted.
|
|
417
|
+
throw new errors_1.SignatureVerificationError('yeria', `no trusted key for kid=${kid}`);
|
|
418
|
+
}
|
|
419
|
+
return JsonApp.verifyUserToken(jwtToken, pem, expectedServiceId);
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Provider-side helper: fetch a Yeria user's profile by `sub`.
|
|
423
|
+
*
|
|
424
|
+
* Builds an Ed25519-signed envelope and POSTs to
|
|
425
|
+
* `POST /api/v1/provider/services/{serviceId}/users/{userId}/profile`.
|
|
426
|
+
* Yeria already holds every Active public key for this service, so
|
|
427
|
+
* the envelope itself is the only credential — no user JWT is sent
|
|
428
|
+
* in the body and the PEM is never disclosed.
|
|
429
|
+
*
|
|
430
|
+
* Typical usage (provider middleware after `verifyUserTokenWithResolver`):
|
|
431
|
+
*
|
|
432
|
+
* let user = await db.users.findByYeriaSub(claims.sub);
|
|
433
|
+
* if (!user) {
|
|
434
|
+
* const profile = await JsonApp.fetchUserProfile({
|
|
435
|
+
* baseUrl: process.env.YERIA_BASE_URL,
|
|
436
|
+
* serviceId: MY_SERVICE_ID,
|
|
437
|
+
* userId: claims.sub,
|
|
438
|
+
* privateKey: SERVICE_ED25519_PRIVATE_KEY,
|
|
439
|
+
* });
|
|
440
|
+
* user = await db.users.insert({ yeria_sub: claims.sub, ...profile });
|
|
441
|
+
* }
|
|
442
|
+
*
|
|
443
|
+
* The hot path stays local-DB after the first miss.
|
|
444
|
+
*/
|
|
445
|
+
static async fetchUserProfile(opts) {
|
|
446
|
+
if (!opts || !opts.baseUrl)
|
|
447
|
+
throw new Error('fetchUserProfile: baseUrl is required');
|
|
448
|
+
if (opts.serviceId === undefined || opts.serviceId === null) {
|
|
449
|
+
throw new Error('fetchUserProfile: serviceId is required');
|
|
450
|
+
}
|
|
451
|
+
if (opts.userId === undefined || opts.userId === null) {
|
|
452
|
+
throw new Error('fetchUserProfile: userId is required');
|
|
453
|
+
}
|
|
454
|
+
if (typeof opts.privateKey !== 'string' || opts.privateKey.length === 0) {
|
|
455
|
+
throw new Error('fetchUserProfile: privateKey (PEM) is required');
|
|
456
|
+
}
|
|
457
|
+
const fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
458
|
+
if (typeof fetchImpl !== 'function') {
|
|
459
|
+
throw new Error('fetchUserProfile: no fetch available — pass opts.fetch on Node < 18 or non-browser runtimes');
|
|
460
|
+
}
|
|
461
|
+
const envelope = {
|
|
462
|
+
service_id: opts.serviceId,
|
|
463
|
+
user_id: opts.userId,
|
|
464
|
+
timestamp: Date.now(),
|
|
465
|
+
nonce: (0, crypto_1.randomBytes)(16).toString('hex'),
|
|
466
|
+
};
|
|
467
|
+
const payloadStr = JSON.stringify(envelope);
|
|
468
|
+
const signature = (0, crypto_1.sign)(null, Buffer.from(payloadStr, 'utf8'), opts.privateKey).toString('base64');
|
|
469
|
+
const baseUrl = opts.baseUrl.replace(/\/+$/, '');
|
|
470
|
+
const url = `${baseUrl}/api/v1/provider/services/${encodeURIComponent(String(opts.serviceId))}/users/${encodeURIComponent(String(opts.userId))}/profile`;
|
|
471
|
+
const res = await fetchImpl(url, {
|
|
472
|
+
method: 'POST',
|
|
473
|
+
headers: { 'Content-Type': 'application/json' },
|
|
474
|
+
body: JSON.stringify({ envelope, signature }),
|
|
475
|
+
});
|
|
476
|
+
let body;
|
|
477
|
+
try {
|
|
478
|
+
body = await res.json();
|
|
479
|
+
}
|
|
480
|
+
catch (_e) {
|
|
481
|
+
body = null;
|
|
482
|
+
}
|
|
483
|
+
if (!res.ok) {
|
|
484
|
+
const msg = extractErrorMessage(body) || `HTTP ${res.status}`;
|
|
485
|
+
throw new Error(`Yeria profile fetch failed: ${msg}`);
|
|
486
|
+
}
|
|
487
|
+
const result = (body && typeof body === 'object' && 'result' in body)
|
|
488
|
+
? body.result
|
|
489
|
+
: body;
|
|
490
|
+
if (!result || typeof result !== 'object') {
|
|
491
|
+
throw new Error('Yeria profile fetch: unexpected response shape');
|
|
492
|
+
}
|
|
493
|
+
const r = result;
|
|
494
|
+
return {
|
|
495
|
+
user_id: Number(r['user_id']),
|
|
496
|
+
first_name: typeof r['first_name'] === 'string' ? r['first_name'] : null,
|
|
497
|
+
last_name: typeof r['last_name'] === 'string' ? r['last_name'] : null,
|
|
498
|
+
country_code: typeof r['country_code'] === 'string' ? r['country_code'] : null,
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
exports.JsonApp = JsonApp;
|
|
503
|
+
function extractErrorMessage(body) {
|
|
504
|
+
if (!body || typeof body !== 'object')
|
|
505
|
+
return null;
|
|
506
|
+
const b = body;
|
|
507
|
+
if (typeof b['message'] === 'string')
|
|
508
|
+
return b['message'];
|
|
509
|
+
if (b['error'] && typeof b['error'] === 'object') {
|
|
510
|
+
const e = b['error'];
|
|
511
|
+
if (typeof e['message'] === 'string')
|
|
512
|
+
return e['message'];
|
|
513
|
+
}
|
|
514
|
+
if (typeof b['error'] === 'string')
|
|
515
|
+
return b['error'];
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
// ── base64url helpers (stateless, no dep) ────────────────────────────────
|
|
519
|
+
// JWT uses base64url (RFC 4648 §5): `-` for `+`, `_` for `/`, no padding.
|
|
520
|
+
function base64UrlDecodeToBuffer(input) {
|
|
521
|
+
const padded = input.replace(/-/g, '+').replace(/_/g, '/') +
|
|
522
|
+
'==='.slice((input.length + 3) % 4);
|
|
523
|
+
return Buffer.from(padded, 'base64');
|
|
524
|
+
}
|
|
525
|
+
function base64UrlDecodeToString(input) {
|
|
526
|
+
return base64UrlDecodeToBuffer(input).toString('utf8');
|
|
527
|
+
}
|
|
528
|
+
//# sourceMappingURL=jsonapp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonapp.js","sourceRoot":"","sources":["../../src/core/jsonapp.ts"],"names":[],"mappings":";;;AAAA,mCAAsF;AAEtF,2CAAuC;AACvC,+CAA2C;AAC3C,yDAAoD;AACpD,yDAAoD;AACpD,iDAA4C;AAC5C,uDAAkD;AAClD,iDAA6C;AAC7C,2CAAuC;AACvC,mDAA+C;AAC/C,mDAA+C;AAC/C,6CAAyC;AACzC,yCAAqC;AACrC,sCAOmB;AACnB,iDAA8C;AA8E9C,MAAa,OAAO;IAKhB,YAAY,MAAqB;QAC7B,IAAI,CAAC,MAAM,GAAG;YACV,cAAc,EAAE,EAAE;YAClB,qBAAqB,EAAE,EAAE;YACzB,GAAG,MAAM;SACZ,CAAC;QAEF,6CAA6C;QAC7C,IAAI,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACxC,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;YACpC,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QACtC,CAAC;aAAM,CAAC;YACJ,0CAA0C;YAC1C,MAAM,OAAO,GAAG,IAAA,4BAAmB,EAAC,SAAS,CAAC,CAAC;YAC/C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAW,CAAC;YACxF,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAW,CAAC;QACzF,CAAC;IACL,CAAC;IAED;;OAEG;IACH,cAAc,CAAC,MAAc,EAAE,KAAa,EAAE,SAAkB;QAC5D,OAAO,IAAI,oBAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IAClD,CAAC;IAED;;OAEG;IACH,gBAAgB,CAAC,MAAc,EAAE,KAAa,EAAE,SAAkB;QAC9D,OAAO,IAAI,wBAAU,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACpD,CAAC;IAED;;OAEG;IACH,oBAAoB,CAAC,MAAc,EAAE,KAAa,EAAE,SAAkB;QAClE,OAAO,IAAI,iCAAc,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACxD,CAAC;IAED;;OAEG;IACH,oBAAoB,CAAC,MAAc,EAAE,KAAa,EAAE,SAAkB;QAClE,OAAO,IAAI,iCAAc,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACxD,CAAC;IAED;;OAEG;IACH,gBAAgB,CAAC,MAAc,EAAE,KAAa,EAAE,SAAkB;QAC9D,OAAO,IAAI,yBAAU,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACpD,CAAC;IAED;;OAEG;IACH,mBAAmB,CAAC,MAAc,EAAE,KAAa,EAAE,SAAkB;QACjE,OAAO,IAAI,+BAAa,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACvD,CAAC;IAED;;OAEG;IACH,iBAAiB,CAAC,MAAc,EAAE,KAAa,EAAE,SAAkB;QAC/D,OAAO,IAAI,0BAAW,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACrD,CAAC;IAED,cAAc,CAAC,MAAc,EAAE,KAAa,EAAE,SAAkB;QAC5D,OAAO,IAAI,oBAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IAClD,CAAC;IAED,kBAAkB,CAAC,MAAc,EAAE,KAAa,EAAE,SAAkB;QAChE,OAAO,IAAI,4BAAY,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACtD,CAAC;IAED,kBAAkB,CAAC,MAAc,EAAE,KAAa,EAAE,SAAkB;QAChE,OAAO,IAAI,4BAAY,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACtD,CAAC;IAED,eAAe,CAAC,MAAc,EAAE,KAAa,EAAE,SAAkB;QAC7D,OAAO,IAAI,sBAAS,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACnD,CAAC;IAED,aAAa,CAAC,MAAc,EAAE,KAAa,EAAE,SAAkB;QAC3D,OAAO,IAAI,kBAAO,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACjD,CAAC;IAED;;OAEG;IACH,kBAAkB,CAAC,MAAc,EAAE,KAAa,EAAE,IAAY,EAAE,IAAa;QACzE,OAAO,IAAI,2BAAY,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACvD,CAAC;IAED;;OAEG;IACH,gBAAgB,CAAC,YAA0B;QACvC,MAAM,gBAAgB,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC;QAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,yFAAyF;QACzF,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC;YAC3B,YAAY,EAAE,gBAAgB;YAC9B,SAAS,EAAE,SAAS;YACpB,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK;SAC3B,CAAC,CAAC;QACH,MAAM,eAAe,GAAG,IAAA,aAAI,EAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAC1E,MAAM,SAAS,GAAG,eAAe,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAErD,OAAO;YACH,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK;YACxB,SAAS;YACT,SAAS;YACT,YAAY,EAAE,gBAAgB;SACjC,CAAC;IACN,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,gBAAgB,CAClB,YAA0B,EAC1B,WAAoB;QAEpB,MAAM,kBAAkB,GAAG,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;QAC/D,MAAM,GAAG,GAAG,WAAW,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;QAEnD,IAAI,CAAC,GAAG,EAAE,CAAC;YACP,MAAM,IAAI,2BAAkB,CAAC,4GAA4G,CAAC,CAAC;QAC/I,CAAC;QAED,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAC9B,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC;gBACxC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,mBAAmB,IAAI,IAAI,CAAC;aACvE,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACf,MAAM,IAAI,sBAAa,CACnB,oBAAW,CAAC,mBAAmB,EAC/B,gCAAgC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,EACxE,OAAO,CACV,CAAC;YACN,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,KAAK,YAAY,sBAAa,IAAI,KAAK,YAAY,2BAAkB,EAAE,CAAC;gBACxE,MAAM,KAAK,CAAC;YAChB,CAAC;YACD,MAAM,IAAI,sBAAa,CACnB,oBAAW,CAAC,mBAAmB,EAC/B,gCAAgC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EACxF,OAAO,EACP,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC7C,CAAC;QACN,CAAC;IACL,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,SAAS,CACX,eAAuB,EACvB,SAA0B,EAC1B,OAAkD;QAElD,IAAI,CAAC,OAAO,EAAE,UAAU,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC;YAC9C,MAAM,IAAI,2BAAkB,CAAC,qDAAqD,CAAC,CAAC;QACxF,CAAC;QAED,MAAM,QAAQ,GAAG;YACb,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC;YAC5B,YAAY,EAAE,OAAO,CAAC,SAAS;YAC/B,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACxB,CAAC;QACF,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,SAAS,GAAG,IAAA,aAAI,EAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAEvF,MAAM,GAAG,GAAG,GAAG,eAAe,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,oBAAoB,SAAS,cAAc,CAAC;QAC9F,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACD,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBACxB,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACjB,QAAQ;oBACR,SAAS;oBACT,gBAAgB,EAAE,IAAI,CAAC,SAAS;iBACnC,CAAC;gBACF,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,mBAAmB,IAAI,IAAI,CAAC;aACvE,CAAC,CAAC;QACP,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,IAAI,sBAAa,CACnB,oBAAW,CAAC,mBAAmB,EAC/B,gCAAgC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EACxF,OAAO,EACP,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC7C,CAAC;QACN,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YACnD,MAAM,IAAI,sBAAa,CACnB,oBAAW,CAAC,mBAAmB,EAC/B,0BAA0B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,IAAI,IAAI,EAAE,EAC1E,OAAO,CACV,CAAC;QACN,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAK/B,CAAC;QAEF,mEAAmE;QACnE,iEAAiE;QACjE,gEAAgE;QAChE,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QAEnC,OAAO;YACH,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,EAAY;YACnC,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,UAAoB;YAC/C,kBAAkB,EAAE,IAAI,CAAC,IAAI,EAAE,oBAAoB,IAAI,CAAC;SAC3D,CAAC;IACN,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,IAAc;QAChB,MAAM,OAAO,GAAmB;YAC5B,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK;YACxB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE;SACtB,CAAC;QACF,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,SAAS,GAAG,IAAA,aAAI,EAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC/F,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,YAAY;QACR,OAAO,IAAI,CAAC,SAAS,CAAC;IAC1B,CAAC;IAED;;;;;;OAMG;IACH,eAAe,CAAC,QAAwB;QACpC,IAAI,CAAC;YACD,+DAA+D;YAC/D,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YAClE,MAAM,OAAO,GAAG,IAAA,eAAM,EAClB,IAAI,EACJ,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,EACrC,IAAI,CAAC,SAAS,EACd,eAAe,CAClB,CAAC;YACF,IAAI,CAAC,OAAO,EAAE,CAAC;gBACX,MAAM,IAAI,mCAA0B,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5D,CAAC;YAED,mCAAmC;YACnC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAmB,CAAC;YAE/D,iBAAiB;YACjB,IAAI,OAAO,CAAC,KAAK,KAAK,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBACtC,MAAM,IAAI,2BAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;YACnE,CAAC;YAED,uBAAuB;YACvB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,qBAAsB,GAAG,EAAE,GAAG,IAAI,CAAC;YACtE,MAAM,GAAG,GAAG,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC;YACpC,IAAI,GAAG,GAAG,cAAc,EAAE,CAAC;gBACvB,MAAM,MAAM,GAAG,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,QAAQ;oBACjD,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAW;oBAC9B,CAAC,CAAC,SAAS,CAAC;gBAChB,MAAM,IAAI,yBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;YAC5D,CAAC;YAED,OAAO,IAAI,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,KAAK,YAAY,2BAAkB;gBACnC,KAAK,YAAY,yBAAgB;gBACjC,KAAK,YAAY,mCAA0B,EAAE,CAAC;gBAC9C,MAAM,KAAK,CAAC;YAChB,CAAC;YACD,MAAM,IAAI,mCAA0B,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5D,CAAC;IACL,CAAC;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,eAAe,CAClB,SAAiB,EACjB,OAAe,EACf,SAAiB,EACjB,OAAgC;QAEhC,IAAI,CAAC;YACD,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YACzD,OAAO,IAAA,eAAM,EAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;QAClF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;YACxE,IAAI,OAAO,EAAE,CAAC;gBACV,OAAO,CAAC,GAAG,CAAC,CAAC;YACjB,CAAC;iBAAM,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,YAAY,EAAE,CAAC;gBACtF,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,GAAG,CAAC,CAAC;YAC/D,CAAC;YACD,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,QAAQ,CACX,IAA6B,EAC7B,KAAa,EACb,UAAkB,EAClB,YAAoB,IAAI,CAAC,GAAG,EAAE;QAE9B,MAAM,OAAO,GAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,SAAS,GAAG,IAAA,aAAI,EAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC1F,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IAClC,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,eAAe,CAClB,QAAgB,EAChB,cAAsB,EACtB,iBAAmC;QAEnC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxD,MAAM,IAAI,mCAA0B,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QACjE,CAAC;QACD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5D,MAAM,IAAI,mCAA0B,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC3B,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAE9B,IAAI,MAA+B,CAAC;QACpC,IAAI,MAAuB,CAAC;QAC5B,IAAI,CAAC;YACD,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,uBAAuB,CAAC,SAAS,CAAC,CAAC,CAAC;YACxD,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAoB,CAAC;QAChF,CAAC;QAAC,OAAO,EAAE,EAAE,CAAC;YACV,MAAM,IAAI,mCAA0B,CAAC,OAAO,EAAE,uBAAuB,CAAC,CAAC;QAC3E,CAAC;QAED,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAI,mCAA0B,CAChC,OAAO,EACP,oBAAoB,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAC9C,CAAC;QACN,CAAC;QAED,sDAAsD;QACtD,MAAM,YAAY,GAAG,GAAG,SAAS,IAAI,UAAU,EAAE,CAAC;QAClD,MAAM,cAAc,GAAG,uBAAuB,CAAC,YAAY,CAAC,CAAC;QAC7D,MAAM,QAAQ,GAAG,IAAA,qBAAY,EAAC,YAAY,CAAC,CAAC;QAC5C,QAAQ,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC9B,QAAQ,CAAC,GAAG,EAAE,CAAC;QACf,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;QAC9D,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,MAAM,IAAI,mCAA0B,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;QACxE,CAAC;QAED,IAAI,MAAM,CAAC,GAAG,KAAK,OAAO,EAAE,CAAC;YACzB,MAAM,IAAI,mCAA0B,CAChC,OAAO,EACP,sBAAsB,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC7C,CAAC;QACN,CAAC;QACD,IAAI,iBAAiB,KAAK,SAAS,IAAI,MAAM,CAAC,GAAG,KAAK,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC;YAC9E,MAAM,IAAI,mCAA0B,CAChC,OAAO,EACP,gCAAgC,MAAM,CAAC,GAAG,cAAc,MAAM,CAAC,iBAAiB,CAAC,EAAE,CACtF,CAAC;QACN,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAC7C,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,GAAG,IAAI,MAAM,EAAE,CAAC;YACzD,MAAM,IAAI,yBAAgB,CAAC,YAAY,EAAE,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC;QACrF,CAAC;QAED,sDAAsD;QACtD,IAAI,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,QAAQ,EAAE,CAAC;YACpC,MAAM,CAAC,GAAG,GAAG,MAAM,CAAC,KAAK,CAAW,CAAC;QACzC,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,KAAK,CAAC,2BAA2B,CACpC,QAAgB,EAChB,QAAgC,EAChC,iBAAmC;QAEnC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxD,MAAM,IAAI,mCAA0B,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QACjE,CAAC;QACD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5D,MAAM,IAAI,mCAA0B,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;QACnE,CAAC;QAED,IAAI,MAA+B,CAAC;QACpC,IAAI,CAAC;YACD,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3D,CAAC;QAAC,OAAO,EAAE,EAAE,CAAC;YACV,MAAM,IAAI,mCAA0B,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,mCAA0B,CAAC,OAAO,EAAE,wBAAwB,CAAC,CAAC;QAC5E,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,CAAC,GAAG,EAAE,CAAC;YACP,8DAA8D;YAC9D,8DAA8D;YAC9D,MAAM,IAAI,mCAA0B,CAChC,OAAO,EACP,0BAA0B,GAAG,EAAE,CAClC,CAAC;QACN,CAAC;QAED,OAAO,OAAO,CAAC,eAAe,CAAC,QAAQ,EAAE,GAAG,EAAE,iBAAiB,CAAC,CAAC;IACrE,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,IAM7B;QACG,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO;YAAK,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QACxF,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YAC1D,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAC/D,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;YACpD,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC5D,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACtE,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC;QACjD,IAAI,OAAO,SAAS,KAAK,UAAU,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CACX,6FAA6F,CAChG,CAAC;QACN,CAAC;QAED,MAAM,QAAQ,GAAG;YACb,UAAU,EAAE,IAAI,CAAC,SAAS;YAC1B,OAAO,EAAE,IAAI,CAAC,MAAM;YACpB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,KAAK,EAAE,IAAA,oBAAW,EAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;SACzC,CAAC;QACF,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC5C,MAAM,SAAS,GAAG,IAAA,aAAI,EAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAElG,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACjD,MAAM,GAAG,GAAG,GAAG,OAAO,6BAA6B,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,UAAU,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC;QAEzJ,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE;YAC7B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;SAChD,CAAC,CAAC;QAEH,IAAI,IAAa,CAAC;QAClB,IAAI,CAAC;YAAC,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAAC,CAAC;QAChC,OAAO,EAAE,EAAE,CAAC;YAAC,IAAI,GAAG,IAAI,CAAC;QAAC,CAAC;QAE3B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACV,MAAM,GAAG,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC;YAC9D,MAAM,IAAI,KAAK,CAAC,+BAA+B,GAAG,EAAE,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,QAAQ,IAAI,IAAI,CAAC;YACjE,CAAC,CAAE,IAA4B,CAAC,MAAM;YACtC,CAAC,CAAC,IAAI,CAAC;QACX,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACtE,CAAC;QACD,MAAM,CAAC,GAAG,MAAiC,CAAC;QAC5C,OAAO;YACH,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YAC7B,UAAU,EAAE,OAAO,CAAC,CAAC,YAAY,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAW,CAAC,CAAC,CAAC,IAAI;YAClF,SAAS,EAAG,OAAO,CAAC,CAAC,WAAW,CAAC,KAAM,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAY,CAAC,CAAC,CAAC,IAAI;YAClF,YAAY,EAAE,OAAO,CAAC,CAAC,cAAc,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAW,CAAC,CAAC,CAAC,IAAI;SAC3F,CAAC;IACN,CAAC;CACJ;AA1lBD,0BA0lBC;AAED,SAAS,mBAAmB,CAAC,IAAa;IACtC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,CAAC,GAAG,IAA+B,CAAC;IAC1C,IAAI,OAAO,CAAC,CAAC,SAAS,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC,SAAS,CAAW,CAAC;IACpE,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC/C,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAA4B,CAAC;QAChD,IAAI,OAAO,CAAC,CAAC,SAAS,CAAC,KAAK,QAAQ;YAAE,OAAO,CAAC,CAAC,SAAS,CAAW,CAAC;IACxE,CAAC;IACD,IAAI,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC,OAAO,CAAW,CAAC;IAChE,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,4EAA4E;AAC5E,0EAA0E;AAE1E,SAAS,uBAAuB,CAAC,KAAa;IAC1C,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC;QACtD,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACxC,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AACzC,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAa;IAC1C,OAAO,uBAAuB,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type KeyState = 'active' | 'rotating' | 'expired' | 'unknown';
|
|
2
|
+
export interface KeyLookup {
|
|
3
|
+
state: KeyState;
|
|
4
|
+
publicKey: string | null;
|
|
5
|
+
expiresAt: string | null;
|
|
6
|
+
}
|
|
7
|
+
export interface YeriaKeyStoreOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Base URL of the Yeria platform — e.g. `https://yeria.app`. Trailing
|
|
10
|
+
* slash optional. The store appends `/api/v1/public/registry/...`.
|
|
11
|
+
*/
|
|
12
|
+
baseUrl: string;
|
|
13
|
+
/**
|
|
14
|
+
* Cache lifetime per kid, in milliseconds. Default 10 minutes.
|
|
15
|
+
* Applies to both positive (PEM) and negative (expired/unknown)
|
|
16
|
+
* cache entries.
|
|
17
|
+
*/
|
|
18
|
+
ttlMs?: number;
|
|
19
|
+
/**
|
|
20
|
+
* Inject a `fetch` implementation. Defaults to the global `fetch` —
|
|
21
|
+
* Node 18+ ships it natively. Tests override to avoid real HTTP.
|
|
22
|
+
*/
|
|
23
|
+
fetch?: typeof fetch;
|
|
24
|
+
}
|
|
25
|
+
export declare class YeriaKeyStore {
|
|
26
|
+
private readonly baseUrl;
|
|
27
|
+
private readonly ttlMs;
|
|
28
|
+
private readonly fetchImpl;
|
|
29
|
+
private readonly cache;
|
|
30
|
+
constructor(opts: YeriaKeyStoreOptions);
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a kid to a PEM. Returns null when the key is expired or
|
|
33
|
+
* unknown — the caller should reject the inbound token in that case.
|
|
34
|
+
*/
|
|
35
|
+
getByKid(kid: string): Promise<string | null>;
|
|
36
|
+
/**
|
|
37
|
+
* Resolve a kid to its full trust state. Same network/cache behaviour
|
|
38
|
+
* as `getByKid` — exposed for callers that want to log / branch on
|
|
39
|
+
* the state.
|
|
40
|
+
*/
|
|
41
|
+
getState(kid: string): Promise<KeyState>;
|
|
42
|
+
/** Drop a single cache entry. Use after a verify failure to force a
|
|
43
|
+
* refetch on the retry — covers the case where Yeria rotated a key
|
|
44
|
+
* out from under our cache. */
|
|
45
|
+
invalidate(kid: string): void;
|
|
46
|
+
/** Drop the entire cache. */
|
|
47
|
+
invalidateAll(): void;
|
|
48
|
+
private lookup;
|
|
49
|
+
private fetchFromYeria;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=key-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-store.d.ts","sourceRoot":"","sources":["../../src/core/key-store.ts"],"names":[],"mappings":"AAyBA,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;AAErE,MAAM,WAAW,SAAS;IACtB,KAAK,EAAE,QAAQ,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACjC;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AASD,qBAAa,aAAa;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;gBAE3C,IAAI,EAAE,oBAAoB;IAetC;;;OAGG;IACG,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAKnD;;;;OAIG;IACG,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAK9C;;oCAEgC;IAChC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI7B,6BAA6B;IAC7B,aAAa,IAAI,IAAI;YAIP,MAAM;YAeN,cAAc;CAuC/B"}
|