@devilsdev/rag-core 3.8.1 → 3.8.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/dist/index.cjs +170 -42
- package/dist/types/config/component-factory.d.ts +4 -2
- package/dist/types/config/load-plugin-config.d.ts +35 -0
- package/dist/types/config/validate-schema.d.ts +18 -0
- package/dist/types/core/plugin-registry.d.ts +13 -0
- package/dist/types/ecosystem/plugin-probe.d.ts +12 -0
- package/dist/types/ecosystem/plugin-scanner.d.ts +9 -1
- package/dist/types/enterprise/audit-logging.d.ts +143 -10
- package/dist/types/guardrails/guardrails-pipeline.d.ts +28 -0
- package/dist/types/retrieval/access-fields.d.ts +30 -0
- package/dist/types/security/plugin-signature-verifier.d.ts +36 -0
- package/dist/types/utils/deprecate.d.ts +23 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -344,6 +344,9 @@ var require_plugin_signature_verifier = __commonJS({
|
|
|
344
344
|
}
|
|
345
345
|
return DIGEST_PREFIX + hash.digest("hex");
|
|
346
346
|
}
|
|
347
|
+
function hasContentDigest(manifest) {
|
|
348
|
+
return Boolean(manifest) && Object.hasOwn(manifest, "contentDigest") && manifest.contentDigest !== void 0 && manifest.contentDigest !== null;
|
|
349
|
+
}
|
|
347
350
|
var PluginSignatureVerifier = class {
|
|
348
351
|
constructor(options = {}) {
|
|
349
352
|
this.enabled = options.enabled !== false;
|
|
@@ -412,37 +415,13 @@ var require_plugin_signature_verifier = __commonJS({
|
|
|
412
415
|
}
|
|
413
416
|
return verificationResult;
|
|
414
417
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
throw new Error("Invalid signature: must be a non-empty string");
|
|
420
|
-
}
|
|
421
|
-
if (!signerId || typeof signerId !== "string") {
|
|
422
|
-
throw new Error("Invalid signerId: must be a non-empty string");
|
|
423
|
-
}
|
|
424
|
-
const trustedKey = this.trustStore.get(signerId);
|
|
425
|
-
if (!trustedKey) {
|
|
426
|
-
throw new Error(`Untrusted signer: ${signerId}`);
|
|
427
|
-
}
|
|
428
|
-
const canonicalManifest = this._canonicalizeManifest(manifest);
|
|
429
|
-
const manifestData = Buffer.from(
|
|
430
|
-
JSON.stringify(canonicalManifest),
|
|
431
|
-
"utf8"
|
|
432
|
-
);
|
|
433
|
-
const signatureBuffer = Buffer.from(signature, "base64");
|
|
434
|
-
const isValid = await this._verifyEd25519Signature(
|
|
435
|
-
manifestData,
|
|
436
|
-
signatureBuffer,
|
|
437
|
-
trustedKey.publicKey
|
|
438
|
-
);
|
|
439
|
-
if (!isValid) {
|
|
440
|
-
throw new Error("Signature verification failed");
|
|
441
|
-
}
|
|
442
|
-
verificationResult.contentVerified = this._verifyContentDigest(
|
|
443
|
-
canonicalManifest,
|
|
418
|
+
const { canonicalManifest, contentVerified } = this.verifyManifestSync(
|
|
419
|
+
manifest,
|
|
420
|
+
signature,
|
|
421
|
+
signerId,
|
|
444
422
|
content
|
|
445
423
|
);
|
|
424
|
+
verificationResult.contentVerified = contentVerified;
|
|
446
425
|
verificationResult.verified = true;
|
|
447
426
|
verificationResult.auditTrail.push({
|
|
448
427
|
action: "signature_verified",
|
|
@@ -470,6 +449,51 @@ var require_plugin_signature_verifier = __commonJS({
|
|
|
470
449
|
this._auditVerification(verificationResult, manifest);
|
|
471
450
|
return verificationResult;
|
|
472
451
|
}
|
|
452
|
+
/**
|
|
453
|
+
* Synchronous signature check shared by every verification path: input
|
|
454
|
+
* validation, trusted-signer lookup, Ed25519 signature over the canonical
|
|
455
|
+
* manifest, and the contentDigest check. Does not consult `enabled` and
|
|
456
|
+
* does not write audit entries; callers decide both.
|
|
457
|
+
*
|
|
458
|
+
* @param {object} manifest - Plugin manifest
|
|
459
|
+
* @param {string} signature - Base64 encoded signature
|
|
460
|
+
* @param {string} signerId - Signer identifier
|
|
461
|
+
* @param {object} [content] - `{ files, baseDir, contents }` to check
|
|
462
|
+
* against manifest.contentDigest (see computeContentDigest)
|
|
463
|
+
* @returns {{contentVerified: boolean, canonicalManifest: object}}
|
|
464
|
+
* contentVerified is false when the manifest has no contentDigest
|
|
465
|
+
* @throws {Error} When the manifest must not be trusted
|
|
466
|
+
*/
|
|
467
|
+
verifyManifestSync(manifest, signature, signerId, content = null) {
|
|
468
|
+
if (!manifest || typeof manifest !== "object") {
|
|
469
|
+
throw new Error("Invalid manifest: must be an object");
|
|
470
|
+
}
|
|
471
|
+
if (!signature || typeof signature !== "string") {
|
|
472
|
+
throw new Error("Invalid signature: must be a non-empty string");
|
|
473
|
+
}
|
|
474
|
+
if (!signerId || typeof signerId !== "string") {
|
|
475
|
+
throw new Error("Invalid signerId: must be a non-empty string");
|
|
476
|
+
}
|
|
477
|
+
const trustedKey = this.trustStore.get(signerId);
|
|
478
|
+
if (!trustedKey) {
|
|
479
|
+
throw new Error(`Untrusted signer: ${signerId}`);
|
|
480
|
+
}
|
|
481
|
+
const canonicalManifest = this._canonicalizeManifest(manifest);
|
|
482
|
+
const manifestData = Buffer.from(JSON.stringify(canonicalManifest), "utf8");
|
|
483
|
+
const signatureBuffer = Buffer.from(signature, "base64");
|
|
484
|
+
if (!this._checkEd25519Signature(
|
|
485
|
+
manifestData,
|
|
486
|
+
signatureBuffer,
|
|
487
|
+
trustedKey.publicKey
|
|
488
|
+
)) {
|
|
489
|
+
throw new Error("Signature verification failed");
|
|
490
|
+
}
|
|
491
|
+
const contentVerified = this._verifyContentDigest(
|
|
492
|
+
canonicalManifest,
|
|
493
|
+
content
|
|
494
|
+
);
|
|
495
|
+
return { contentVerified, canonicalManifest };
|
|
496
|
+
}
|
|
473
497
|
/**
|
|
474
498
|
* Check the supplied plugin content against the signed contentDigest.
|
|
475
499
|
* @param {object} canonicalManifest - Signed manifest
|
|
@@ -478,9 +502,9 @@ var require_plugin_signature_verifier = __commonJS({
|
|
|
478
502
|
* @private
|
|
479
503
|
*/
|
|
480
504
|
_verifyContentDigest(canonicalManifest, content) {
|
|
481
|
-
const declared =
|
|
505
|
+
const declared = hasContentDigest(canonicalManifest) ? canonicalManifest.contentDigest : void 0;
|
|
482
506
|
const hasContent = content && (Array.isArray(content.files) && content.files.length > 0 || content.contents && Object.keys(content.contents).length > 0);
|
|
483
|
-
if (declared === void 0
|
|
507
|
+
if (declared === void 0) {
|
|
484
508
|
if (this.requireContentDigest) {
|
|
485
509
|
throw new Error(
|
|
486
510
|
"Manifest has no contentDigest; the signature does not cover the plugin code"
|
|
@@ -514,6 +538,17 @@ var require_plugin_signature_verifier = __commonJS({
|
|
|
514
538
|
* @returns {Promise<boolean>} Verification result
|
|
515
539
|
*/
|
|
516
540
|
async _verifyEd25519Signature(data, signature, publicKey) {
|
|
541
|
+
return this._checkEd25519Signature(data, signature, publicKey);
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Synchronous form of _verifyEd25519Signature.
|
|
545
|
+
* @param {Buffer} data - Data that was signed
|
|
546
|
+
* @param {Buffer} signature - Signature bytes
|
|
547
|
+
* @param {crypto.KeyObject|Buffer|string} publicKey - Ed25519 public key
|
|
548
|
+
* @returns {boolean}
|
|
549
|
+
* @private
|
|
550
|
+
*/
|
|
551
|
+
_checkEd25519Signature(data, signature, publicKey) {
|
|
517
552
|
const keyObject = toEd25519PublicKey(publicKey);
|
|
518
553
|
if (!Buffer.isBuffer(signature) || signature.length !== 64) {
|
|
519
554
|
return false;
|
|
@@ -695,6 +730,7 @@ var require_plugin_signature_verifier = __commonJS({
|
|
|
695
730
|
PluginSignatureVerifier,
|
|
696
731
|
createPluginVerifier,
|
|
697
732
|
computeContentDigest,
|
|
733
|
+
hasContentDigest,
|
|
698
734
|
toEd25519PublicKey
|
|
699
735
|
};
|
|
700
736
|
}
|
|
@@ -1137,6 +1173,23 @@ ${errorMessage}`
|
|
|
1137
1173
|
isSignatureVerificationEnabled() {
|
|
1138
1174
|
return Boolean(this._signatureVerifier && this._signatureVerifier.enabled);
|
|
1139
1175
|
}
|
|
1176
|
+
/**
|
|
1177
|
+
* The signature verifier this registry uses (trust store, policy flags).
|
|
1178
|
+
* @returns {object} The PluginSignatureVerifier
|
|
1179
|
+
*/
|
|
1180
|
+
getSignatureVerifier() {
|
|
1181
|
+
return this._signatureVerifier;
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* Record an audit entry for a plugin verification decision made outside the
|
|
1185
|
+
* registry (for example when a configured plugin is checked before load).
|
|
1186
|
+
* @param {string} action - Audit action
|
|
1187
|
+
* @param {object} details - Audit details
|
|
1188
|
+
* @returns {void}
|
|
1189
|
+
*/
|
|
1190
|
+
recordAuditEntry(action, details) {
|
|
1191
|
+
this._emitAuditEntry(action, details);
|
|
1192
|
+
}
|
|
1140
1193
|
/**
|
|
1141
1194
|
* Whether plugins without a signed manifest are rejected.
|
|
1142
1195
|
* @returns {boolean}
|
|
@@ -1384,7 +1437,7 @@ var require_package = __commonJS({
|
|
|
1384
1437
|
"package.json"(exports2, module2) {
|
|
1385
1438
|
module2.exports = {
|
|
1386
1439
|
name: "@devilsdev/rag-pipeline-utils",
|
|
1387
|
-
version: "3.8.
|
|
1440
|
+
version: "3.8.2",
|
|
1388
1441
|
description: "Composable retrieval-augmented generation (RAG) for Node.js: grounded citations, evaluation, guardrails, hybrid retrieval and a CLI.",
|
|
1389
1442
|
main: "dist/index.cjs",
|
|
1390
1443
|
type: "commonjs",
|
|
@@ -1643,10 +1696,10 @@ var require_package = __commonJS({
|
|
|
1643
1696
|
"why-is-node-running": "^3.2.2"
|
|
1644
1697
|
},
|
|
1645
1698
|
dependencies: {
|
|
1646
|
-
"@devilsdev/rag-connectors": "3.8.
|
|
1647
|
-
"@devilsdev/rag-core": "3.8.
|
|
1648
|
-
"@devilsdev/rag-eval": "3.8.
|
|
1649
|
-
"@devilsdev/rag-guardrails": "3.8.
|
|
1699
|
+
"@devilsdev/rag-connectors": "3.8.2",
|
|
1700
|
+
"@devilsdev/rag-core": "3.8.2",
|
|
1701
|
+
"@devilsdev/rag-eval": "3.8.2",
|
|
1702
|
+
"@devilsdev/rag-guardrails": "3.8.2",
|
|
1650
1703
|
ajv: "^8.17.1",
|
|
1651
1704
|
axios: "^1.21.0",
|
|
1652
1705
|
chokidar: "^4.0.3",
|
|
@@ -2917,18 +2970,21 @@ var require_scoring = __commonJS({
|
|
|
2917
2970
|
`\\b(${REFERENCE_WORDS})\\.?\\s+\\d+[a-z]?\\b`,
|
|
2918
2971
|
"giu"
|
|
2919
2972
|
);
|
|
2920
|
-
var IDENTIFIER_RE = new RegExp("(\\p{L}[\\p{L}\\p{N}]*)-(\\d+)\\b", "gu");
|
|
2973
|
+
var IDENTIFIER_RE = new RegExp("(?<![\\p{L}\\p{N}])(\\p{N}*)(\\p{L}[\\p{L}\\p{N}]*)-(\\d+)\\b", "gu");
|
|
2921
2974
|
function identifiers(text) {
|
|
2922
2975
|
if (!text || typeof text !== "string") return [];
|
|
2923
2976
|
return [
|
|
2924
2977
|
...new Set(
|
|
2925
|
-
[...text.matchAll(IDENTIFIER_RE)].map(
|
|
2978
|
+
[...text.matchAll(IDENTIFIER_RE)].map(
|
|
2979
|
+
// m[1] is digits before the word ("2kinesin-8"), not part of it
|
|
2980
|
+
(m) => `id:${m[0].slice(m[1].length).toLowerCase()}`
|
|
2981
|
+
)
|
|
2926
2982
|
)
|
|
2927
2983
|
];
|
|
2928
2984
|
}
|
|
2929
2985
|
function withoutReferences(text) {
|
|
2930
2986
|
if (!text || typeof text !== "string") return text;
|
|
2931
|
-
return text.replace(REFERENCE_RE, " ").replace(IDENTIFIER_RE, (_, word) => word);
|
|
2987
|
+
return text.replace(REFERENCE_RE, " ").replace(IDENTIFIER_RE, (_, lead, word) => lead + word);
|
|
2932
2988
|
}
|
|
2933
2989
|
var MONTHS = [
|
|
2934
2990
|
["january", "jan"],
|
|
@@ -5245,13 +5301,82 @@ var require_chunking_engine = __commonJS({
|
|
|
5245
5301
|
var require_access_fields = __commonJS({
|
|
5246
5302
|
"src/retrieval/access-fields.js"(exports2, module2) {
|
|
5247
5303
|
"use strict";
|
|
5304
|
+
var ACCESS_KEYS = ["requiredRole", "acl"];
|
|
5305
|
+
var STORED_KEY = "rag_access";
|
|
5306
|
+
var NO_ACCESS = "rag:no-access";
|
|
5248
5307
|
function accessFields(doc) {
|
|
5249
5308
|
const fields = {};
|
|
5250
5309
|
if (doc.acl !== void 0) fields.acl = doc.acl;
|
|
5251
5310
|
if (doc.requiredRole !== void 0) fields.requiredRole = doc.requiredRole;
|
|
5252
5311
|
return fields;
|
|
5253
5312
|
}
|
|
5254
|
-
|
|
5313
|
+
function storedMetadata(doc) {
|
|
5314
|
+
const metadata = doc.metadata || {};
|
|
5315
|
+
const access = accessFields(doc);
|
|
5316
|
+
return Object.keys(access).length ? { ...metadata, [STORED_KEY]: access } : metadata;
|
|
5317
|
+
}
|
|
5318
|
+
function readStoredMetadata(stored) {
|
|
5319
|
+
if (!stored || typeof stored !== "object" || !(STORED_KEY in stored)) {
|
|
5320
|
+
return { metadata: stored || {}, access: {} };
|
|
5321
|
+
}
|
|
5322
|
+
const { [STORED_KEY]: access, ...metadata } = stored;
|
|
5323
|
+
const readable = access && typeof access === "object" && !Array.isArray(access);
|
|
5324
|
+
return {
|
|
5325
|
+
metadata,
|
|
5326
|
+
access: readable ? accessFields(access) : { acl: [NO_ACCESS] }
|
|
5327
|
+
};
|
|
5328
|
+
}
|
|
5329
|
+
function restrictionLists(result) {
|
|
5330
|
+
const lists = [];
|
|
5331
|
+
if (!result || typeof result !== "object") return lists;
|
|
5332
|
+
for (const source of [result, result.metadata, result.doc]) {
|
|
5333
|
+
if (!source || typeof source !== "object") continue;
|
|
5334
|
+
for (const key of ACCESS_KEYS) {
|
|
5335
|
+
const value = source[key];
|
|
5336
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
5337
|
+
const roles = [].concat(value);
|
|
5338
|
+
if (roles.length > 0) lists.push(roles);
|
|
5339
|
+
}
|
|
5340
|
+
}
|
|
5341
|
+
return lists;
|
|
5342
|
+
}
|
|
5343
|
+
var sameRoles = (a, b) => a.length === b.length && a.every((role) => b.includes(role));
|
|
5344
|
+
function mergeAccess(first, others) {
|
|
5345
|
+
const have = restrictionLists(first);
|
|
5346
|
+
const extra = [];
|
|
5347
|
+
for (const copy of others) {
|
|
5348
|
+
for (const roles of restrictionLists(copy)) {
|
|
5349
|
+
if (![...have, ...extra].some((list) => sameRoles(list, roles))) {
|
|
5350
|
+
extra.push(roles);
|
|
5351
|
+
}
|
|
5352
|
+
}
|
|
5353
|
+
}
|
|
5354
|
+
if (extra.length === 0) return first;
|
|
5355
|
+
const merged = { ...first };
|
|
5356
|
+
const free = ACCESS_KEYS.filter(
|
|
5357
|
+
(key) => restrictionLists({ [key]: merged[key] }).length === 0
|
|
5358
|
+
);
|
|
5359
|
+
if (extra.length <= free.length) {
|
|
5360
|
+
extra.forEach((roles, i) => {
|
|
5361
|
+
merged[free[i]] = roles;
|
|
5362
|
+
});
|
|
5363
|
+
return merged;
|
|
5364
|
+
}
|
|
5365
|
+
const lists = [...restrictionLists(accessFields(merged)), ...extra];
|
|
5366
|
+
const shared = lists[0].filter(
|
|
5367
|
+
(role) => lists.every((list) => list.includes(role))
|
|
5368
|
+
);
|
|
5369
|
+
delete merged.requiredRole;
|
|
5370
|
+
merged.acl = shared.length ? shared : [NO_ACCESS];
|
|
5371
|
+
return merged;
|
|
5372
|
+
}
|
|
5373
|
+
module2.exports = {
|
|
5374
|
+
NO_ACCESS,
|
|
5375
|
+
accessFields,
|
|
5376
|
+
storedMetadata,
|
|
5377
|
+
readStoredMetadata,
|
|
5378
|
+
mergeAccess
|
|
5379
|
+
};
|
|
5255
5380
|
}
|
|
5256
5381
|
});
|
|
5257
5382
|
|
|
@@ -5392,6 +5517,7 @@ var require_bm25_search = __commonJS({
|
|
|
5392
5517
|
var require_rank_fusion = __commonJS({
|
|
5393
5518
|
"src/retrieval/rank-fusion.js"(exports2, module2) {
|
|
5394
5519
|
"use strict";
|
|
5520
|
+
var { mergeAccess } = require_access_fields();
|
|
5395
5521
|
var DEFAULT_CONFIG = { k: 60 };
|
|
5396
5522
|
function reciprocalRankFusion(resultSets, options = {}) {
|
|
5397
5523
|
const { k = DEFAULT_CONFIG.k, weights } = options;
|
|
@@ -5415,15 +5541,17 @@ var require_rank_fusion = __commonJS({
|
|
|
5415
5541
|
if (scoreMap.has(id)) {
|
|
5416
5542
|
const entry = scoreMap.get(id);
|
|
5417
5543
|
entry.score += rrfScore;
|
|
5544
|
+
entry.copies.push(doc);
|
|
5418
5545
|
} else {
|
|
5419
5546
|
const { score: _origScore, ...rest } = doc;
|
|
5420
|
-
scoreMap.set(id, { score: rrfScore, fields: rest });
|
|
5547
|
+
scoreMap.set(id, { score: rrfScore, fields: rest, copies: [] });
|
|
5421
5548
|
}
|
|
5422
5549
|
}
|
|
5423
5550
|
}
|
|
5424
5551
|
const fused = [];
|
|
5425
5552
|
for (const [id, entry] of scoreMap) {
|
|
5426
|
-
|
|
5553
|
+
const fields = mergeAccess(entry.fields, entry.copies);
|
|
5554
|
+
fused.push({ ...fields, id, score: entry.score });
|
|
5427
5555
|
}
|
|
5428
5556
|
fused.sort((a, b) => b.score - a.score);
|
|
5429
5557
|
return fused;
|
|
@@ -24,9 +24,11 @@ export function buildComponents(config: object, options?: {
|
|
|
24
24
|
/**
|
|
25
25
|
* Instantiate a custom plugin from `plugins.<stage>.<name>` in the config.
|
|
26
26
|
* The module may export a class (constructed with the entry config), an
|
|
27
|
-
* instance, or either of those as `default`.
|
|
27
|
+
* instance, or either of those as `default`. When the shared plugin registry
|
|
28
|
+
* has signature verification enabled (production by default), the module is
|
|
29
|
+
* verified against its signed manifest before it is loaded.
|
|
28
30
|
*/
|
|
29
|
-
export function loadPluginModule(modulePath: any, entryConfig: any, configDir: any): any;
|
|
31
|
+
export function loadPluginModule(modulePath: any, entryConfig: any, configDir: any, name: any): any;
|
|
30
32
|
export namespace BUILT_INS {
|
|
31
33
|
namespace loader {
|
|
32
34
|
function auto(): typeof import("../loader/document-reader");
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loads plugins from validated _config path and registers them.
|
|
3
|
+
* @param {string} configPath
|
|
4
|
+
*/
|
|
5
|
+
export function loadPluginsFromJson(configPath: string): Promise<void>;
|
|
6
|
+
/**
|
|
7
|
+
* Verify a plugin file against its signed manifest BEFORE it is imported
|
|
8
|
+
* (importing runs the plugin's code).
|
|
9
|
+
*
|
|
10
|
+
* The manifest is read from `<plugin file>.manifest.json`. Its
|
|
11
|
+
* `contentDigest` (if any) must cover the plugin file plus any extra files
|
|
12
|
+
* listed in `manifest.files` (paths relative to the plugin file's
|
|
13
|
+
* directory). Nothing is checked when the registry's signature verification
|
|
14
|
+
* is disabled; when it is enabled and the registry was created with
|
|
15
|
+
* `requireSignedPlugins: true`, a missing manifest is an error.
|
|
16
|
+
*
|
|
17
|
+
* @param {string} name - Plugin name
|
|
18
|
+
* @param {string} absPath - Absolute path of the plugin entry file
|
|
19
|
+
* @returns {Promise<void>}
|
|
20
|
+
*/
|
|
21
|
+
export function verifyPluginBeforeImport(name: string, absPath: string): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Synchronous form of verifyPluginBeforeImport for loaders that `require`
|
|
24
|
+
* the plugin. Same policy: nothing happens while the registry's signature
|
|
25
|
+
* verification is disabled; otherwise a plugin with a manifest must have a
|
|
26
|
+
* valid signature from a trusted signer and matching content, and a plugin
|
|
27
|
+
* without one loads with a one-time warning unless the registry requires
|
|
28
|
+
* signed plugins. Every check completes before any plugin code runs.
|
|
29
|
+
*
|
|
30
|
+
* @param {string} name - Plugin name
|
|
31
|
+
* @param {string} absPath - Absolute path of the file that will be loaded
|
|
32
|
+
* @returns {void}
|
|
33
|
+
* @throws {Error} When the plugin must not be loaded
|
|
34
|
+
*/
|
|
35
|
+
export function verifyPluginBeforeLoad(name: string, absPath: string): void;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates the full .ragrc.json _config structure
|
|
3
|
+
* @param {object} config
|
|
4
|
+
* @returns {{ valid: boolean, errors?: any[] }}
|
|
5
|
+
*/
|
|
6
|
+
export function validateRagrcSchema(config: object): {
|
|
7
|
+
valid: boolean;
|
|
8
|
+
errors?: any[];
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Validates a plugin-only structure (for runtime plugin loading)
|
|
12
|
+
* @param {object} config
|
|
13
|
+
* @returns {{ valid: boolean, errors?: any[] }}
|
|
14
|
+
*/
|
|
15
|
+
export function validatePluginSchema(config: object): {
|
|
16
|
+
valid: boolean;
|
|
17
|
+
errors?: any[];
|
|
18
|
+
};
|
|
@@ -113,6 +113,19 @@ declare class PluginRegistry {
|
|
|
113
113
|
* @returns {boolean}
|
|
114
114
|
*/
|
|
115
115
|
isSignatureVerificationEnabled(): boolean;
|
|
116
|
+
/**
|
|
117
|
+
* The signature verifier this registry uses (trust store, policy flags).
|
|
118
|
+
* @returns {object} The PluginSignatureVerifier
|
|
119
|
+
*/
|
|
120
|
+
getSignatureVerifier(): object;
|
|
121
|
+
/**
|
|
122
|
+
* Record an audit entry for a plugin verification decision made outside the
|
|
123
|
+
* registry (for example when a configured plugin is checked before load).
|
|
124
|
+
* @param {string} action - Audit action
|
|
125
|
+
* @param {object} details - Audit details
|
|
126
|
+
* @returns {void}
|
|
127
|
+
*/
|
|
128
|
+
recordAuditEntry(action: string, details: object): void;
|
|
116
129
|
/**
|
|
117
130
|
* Whether plugins without a signed manifest are rejected.
|
|
118
131
|
* @returns {boolean}
|
|
@@ -12,6 +12,18 @@ export function probeContract(modulePath: string, stage: string, options?: {
|
|
|
12
12
|
methods: string[];
|
|
13
13
|
error?: string;
|
|
14
14
|
}>;
|
|
15
|
+
/**
|
|
16
|
+
* Environment for the probe process: only what Node needs to start, so the
|
|
17
|
+
* plugin code being loaded does not see the caller's other variables. HOME,
|
|
18
|
+
* NODE_OPTIONS and NODE_PATH are left out on purpose.
|
|
19
|
+
*
|
|
20
|
+
* @param {string} [platform] - Defaults to process.platform
|
|
21
|
+
* @param {object} [source] - Defaults to process.env
|
|
22
|
+
* @returns {Object<string, string>}
|
|
23
|
+
*/
|
|
24
|
+
export function probeEnvironment(platform?: string, source?: object): {
|
|
25
|
+
[x: string]: string;
|
|
26
|
+
};
|
|
15
27
|
export namespace STAGE_METHODS {
|
|
16
28
|
let loader: string[];
|
|
17
29
|
let embedder: string[];
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Besides files with a source extension, the package entry file and any
|
|
3
|
+
* file a scanned file loads by relative path are scanned whatever their
|
|
4
|
+
* extension, because Node runs them as JavaScript.
|
|
5
|
+
*
|
|
2
6
|
* @param {string} pluginPath - Plugin directory or single .js file
|
|
7
|
+
* @param {object} [options]
|
|
8
|
+
* @param {string} [options.entry] - Package entry file (absolute or relative to pluginPath)
|
|
3
9
|
* @returns {{
|
|
4
10
|
* findings: Array<{severity: string, rule: string, message: string, file: string, line?: number}>,
|
|
5
11
|
* counts: {high: number, medium: number, low: number},
|
|
6
12
|
* filesScanned: number
|
|
7
13
|
* }}
|
|
8
14
|
*/
|
|
9
|
-
export function scanPlugin(pluginPath: string
|
|
15
|
+
export function scanPlugin(pluginPath: string, options?: {
|
|
16
|
+
entry?: string;
|
|
17
|
+
}): {
|
|
10
18
|
findings: Array<{
|
|
11
19
|
severity: string;
|
|
12
20
|
rule: string;
|
|
@@ -65,8 +65,11 @@ export class AuditLogger extends EventEmitter {
|
|
|
65
65
|
};
|
|
66
66
|
logBuffer: any[];
|
|
67
67
|
encryptionKey: string | Buffer<ArrayBufferLike>;
|
|
68
|
-
signingKey:
|
|
69
|
-
|
|
68
|
+
signingKey: string | Buffer<ArrayBufferLike>;
|
|
69
|
+
_chainState: any;
|
|
70
|
+
_chainLoad: any;
|
|
71
|
+
_chainDirty: boolean;
|
|
72
|
+
_flushQueue: Promise<void>;
|
|
70
73
|
/**
|
|
71
74
|
* Initialize audit logging system
|
|
72
75
|
*/
|
|
@@ -105,9 +108,20 @@ export class AuditLogger extends EventEmitter {
|
|
|
105
108
|
*/
|
|
106
109
|
_writeAuditLog(auditEvent: any): Promise<any>;
|
|
107
110
|
/**
|
|
108
|
-
* Flush logs to persistent storage
|
|
111
|
+
* Flush logs to persistent storage. Flushes run one at a time, so records
|
|
112
|
+
* are numbered and chained in the order they are appended.
|
|
109
113
|
*/
|
|
110
114
|
_flushLogs(): Promise<void>;
|
|
115
|
+
_flushOnce(): Promise<void>;
|
|
116
|
+
_endsMidLine(file: any): Promise<boolean>;
|
|
117
|
+
_stripChainFields(log: any): void;
|
|
118
|
+
/**
|
|
119
|
+
* Give each record its sequence, the link to the previous record, the key
|
|
120
|
+
* id, the integrity hash and (when signing is on) the signature, advancing
|
|
121
|
+
* `state` as it goes.
|
|
122
|
+
* @private
|
|
123
|
+
*/
|
|
124
|
+
private _numberRecords;
|
|
111
125
|
/**
|
|
112
126
|
* Query audit logs
|
|
113
127
|
*/
|
|
@@ -115,13 +129,14 @@ export class AuditLogger extends EventEmitter {
|
|
|
115
129
|
results: any[];
|
|
116
130
|
total: number;
|
|
117
131
|
hasMore: boolean;
|
|
132
|
+
skippedLines: number;
|
|
118
133
|
}>;
|
|
119
134
|
/**
|
|
120
135
|
* Generate compliance report
|
|
121
136
|
*/
|
|
122
137
|
generateComplianceReport(_options: any): Promise<{
|
|
123
138
|
metadata: {
|
|
124
|
-
_tenantId:
|
|
139
|
+
_tenantId: string;
|
|
125
140
|
standard: any;
|
|
126
141
|
period: {
|
|
127
142
|
startDate: any;
|
|
@@ -148,7 +163,24 @@ export class AuditLogger extends EventEmitter {
|
|
|
148
163
|
evidence: any[];
|
|
149
164
|
}>;
|
|
150
165
|
/**
|
|
151
|
-
* Verify log integrity
|
|
166
|
+
* Verify log integrity.
|
|
167
|
+
*
|
|
168
|
+
* Besides the per-record checks it reads the chain: every record carries its
|
|
169
|
+
* sequence, a link to the previous record and a keyed hash over the whole
|
|
170
|
+
* record, and the chain file holds the latest state.
|
|
171
|
+
*
|
|
172
|
+
* - `integrityBreaks`: `{ file, line, sequence, reason }`, where reason is
|
|
173
|
+
* `edited`, `chain`, `order`, `tail`, `unparseable`, `chain file missing`,
|
|
174
|
+
* `chain file reset` or `chain file forged`.
|
|
175
|
+
* - `missingLogs`: gaps in the sequence, `{ after, before, count }`.
|
|
176
|
+
* - `unchained`: records written before chain links existed (counted, and
|
|
177
|
+
* they do not make `verified` false).
|
|
178
|
+
* - `otherKey`: records written with another signing key; they can't be
|
|
179
|
+
* checked, so `verified` is false.
|
|
180
|
+
* - `secondWriter`: duplicate or interleaved sequences that look like two
|
|
181
|
+
* writers on one log directory. A break is only reported here when every
|
|
182
|
+
* record involved verifies and every interleaved run links up on its own;
|
|
183
|
+
* otherwise it is reported as the tampering it may be.
|
|
152
184
|
*/
|
|
153
185
|
verifyIntegrity(_options?: {}): Promise<{
|
|
154
186
|
verified: boolean;
|
|
@@ -157,6 +189,15 @@ export class AuditLogger extends EventEmitter {
|
|
|
157
189
|
tamperedLogs: any[];
|
|
158
190
|
missingLogs: any[];
|
|
159
191
|
integrityBreaks: any[];
|
|
192
|
+
unchained: {
|
|
193
|
+
count: number;
|
|
194
|
+
ranges: any[];
|
|
195
|
+
};
|
|
196
|
+
otherKey: {
|
|
197
|
+
count: number;
|
|
198
|
+
ranges: any[];
|
|
199
|
+
};
|
|
200
|
+
secondWriter: any[];
|
|
160
201
|
}>;
|
|
161
202
|
_calculateIntegrityHash(auditEvent: any): Promise<string>;
|
|
162
203
|
_encryptSensitiveFields(auditEvent: any): Promise<any>;
|
|
@@ -190,9 +231,89 @@ export class AuditLogger extends EventEmitter {
|
|
|
190
231
|
*/
|
|
191
232
|
_getCipherKey(): Buffer<ArrayBufferLike>;
|
|
192
233
|
_createDecipher(encryptedField: any): crypto.DecipherGCM;
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
234
|
+
/**
|
|
235
|
+
* Resolve the key behind the integrity hashes, chain links and signatures.
|
|
236
|
+
*
|
|
237
|
+
* Order: the `signingKey` option, then the RAG_AUDIT_SIGNING_KEY env var,
|
|
238
|
+
* then a key stored in `<logDir>/integrity/signing.key` (created on first
|
|
239
|
+
* use), and only if that file can't be used a random per-process key. A
|
|
240
|
+
* stored key lets the logs be checked after a restart, but anyone who can
|
|
241
|
+
* read the log folder can read it, so it only detects accidental damage and
|
|
242
|
+
* casual edits. Both of the last two warn once per process.
|
|
243
|
+
*
|
|
244
|
+
* @param {Object} _options - Constructor options
|
|
245
|
+
* @returns {Buffer|string} Key material
|
|
246
|
+
* @private
|
|
247
|
+
*/
|
|
248
|
+
private _resolveSigningKey;
|
|
249
|
+
/**
|
|
250
|
+
* The key stored in `<logDir>/integrity/signing.key` as 128 hex digits and
|
|
251
|
+
* a newline, created if it doesn't exist yet. It is created with the 'wx'
|
|
252
|
+
* flag, so of several processes starting together exactly one writes it and
|
|
253
|
+
* the rest read what it wrote (briefly retrying while it is still empty).
|
|
254
|
+
* Modes are owner-only (0o600 file, 0o700 directory); Windows ignores POSIX
|
|
255
|
+
* modes. A file that exists but is unreadable or invalid is never
|
|
256
|
+
* overwritten. It lives next to chain.json, not under daily/, so it is never
|
|
257
|
+
* read as a log file.
|
|
258
|
+
* @returns {{key: ?Buffer, file: string, reason?: string}}
|
|
259
|
+
* @private
|
|
260
|
+
*/
|
|
261
|
+
private _storedSigningKey;
|
|
262
|
+
_hmac(text: any): string;
|
|
263
|
+
_keyId(): string;
|
|
264
|
+
_genesisLink(chainId: any): string;
|
|
265
|
+
_chainLink(chainPrev: any, integrity: any): string;
|
|
266
|
+
_stateTag(chainId: any, sequence: any, lastHash: any): string;
|
|
267
|
+
_chainFilePath(): string;
|
|
268
|
+
/**
|
|
269
|
+
* Read chain.json. Status is `missing`, `unreadable`, `legacy` (a file from
|
|
270
|
+
* before 3.8.2), `invalid` (wrong shape, or a state tag that doesn't match
|
|
271
|
+
* under this key), `otherKey` (written with another signing key) or `ok`.
|
|
272
|
+
* @private
|
|
273
|
+
*/
|
|
274
|
+
private _readChainFile;
|
|
275
|
+
/**
|
|
276
|
+
* The chain state (chain id, last sequence and link), loaded once, at the
|
|
277
|
+
* first write.
|
|
278
|
+
* @private
|
|
279
|
+
*/
|
|
280
|
+
private _loadChainState;
|
|
281
|
+
/**
|
|
282
|
+
* Work out where the chain stands before the first write of this process.
|
|
283
|
+
*
|
|
284
|
+
* - chain.json is valid and the newest records don't go past it: continue.
|
|
285
|
+
* - Records go past it and link from it (the process ended after the
|
|
286
|
+
* file was appended to but before chain.json was rewritten): advance the state and continue.
|
|
287
|
+
* - Otherwise (chain.json missing, replaced, not valid under this key, or
|
|
288
|
+
* records that don't link) a new chain segment starts. It is never
|
|
289
|
+
* silent: it is recorded in chain.json `resets`, logged as a warning and
|
|
290
|
+
* emitted as `chain_reset`, and the sequence carries on from the highest
|
|
291
|
+
* one in the files. A chain problem never blocks logging.
|
|
292
|
+
* @private
|
|
293
|
+
*/
|
|
294
|
+
private _recoverChainState;
|
|
295
|
+
/** Chained records of the newest daily file that has any, in file order. */
|
|
296
|
+
_newestChainedRecords(files: any): Promise<any[]>;
|
|
297
|
+
/**
|
|
298
|
+
* Move `state` forward over `records` if they continue it exactly: this
|
|
299
|
+
* key, consecutive sequences, each linked to the one before and with a
|
|
300
|
+
* valid hash. Leaves `state` alone otherwise.
|
|
301
|
+
* @private
|
|
302
|
+
*/
|
|
303
|
+
private _advanceThrough;
|
|
304
|
+
/**
|
|
305
|
+
* Start a chain: fresh when no chained records exist yet (keeping the
|
|
306
|
+
* values of a pre-3.8.2 chain.json under `legacy`), or a new segment that
|
|
307
|
+
* continues the sequence when they do.
|
|
308
|
+
* @private
|
|
309
|
+
*/
|
|
310
|
+
private _startChainSegment;
|
|
311
|
+
/**
|
|
312
|
+
* Write chain.json with the latest state. If the write fails the state is
|
|
313
|
+
* kept and written on the next flush.
|
|
314
|
+
* @private
|
|
315
|
+
*/
|
|
316
|
+
private _persistChainState;
|
|
196
317
|
_signAuditEvent(auditEvent: any): Promise<string>;
|
|
197
318
|
_verifySignature(auditEvent: any): Promise<boolean>;
|
|
198
319
|
/**
|
|
@@ -203,14 +324,26 @@ export class AuditLogger extends EventEmitter {
|
|
|
203
324
|
* @private
|
|
204
325
|
*/
|
|
205
326
|
private _getLogFilesInRange;
|
|
206
|
-
|
|
327
|
+
/**
|
|
328
|
+
* Every non-blank line of a daily file (queryLogs() counts the unreadable ones as skippedLines) as `{ line, record }`, where `record`
|
|
329
|
+
* is null when the line isn't a JSON object (a half-written or damaged
|
|
330
|
+
* line). `line` counts from 1.
|
|
331
|
+
*/
|
|
332
|
+
_readLogEntries(filepath: any): Promise<any[]>;
|
|
333
|
+
/**
|
|
334
|
+
* The tenant a query asks for, or undefined when it names none. A tenantId
|
|
335
|
+
* (or the underscore form `_tenantId`, read only when `tenantId` is absent)
|
|
336
|
+
* that is present must be a string: null, undefined and other types used to
|
|
337
|
+
* match every tenant.
|
|
338
|
+
* @private
|
|
339
|
+
*/
|
|
340
|
+
private _resolveTenantFilter;
|
|
207
341
|
_matchesQuery(log: any, query: any): boolean;
|
|
208
342
|
_checkCompliance(log: any, standard: any): {
|
|
209
343
|
requirement: any;
|
|
210
344
|
violation: boolean;
|
|
211
345
|
evidence: any;
|
|
212
346
|
};
|
|
213
|
-
_sendToWebhook(logs: any): Promise<void>;
|
|
214
347
|
/**
|
|
215
348
|
* Cleanup old logs based on retention policy
|
|
216
349
|
*/
|
|
@@ -21,6 +21,12 @@ export class GuardrailsPipeline extends EventEmitter {
|
|
|
21
21
|
/**
|
|
22
22
|
* Run the full guarded pipeline.
|
|
23
23
|
*
|
|
24
|
+
* With `options.stream` and a post-generation guard, the token stream is
|
|
25
|
+
* buffered, checked and redacted as a whole, then released as an async
|
|
26
|
+
* iterable: one token chunk with the sanitized answer, then a done chunk
|
|
27
|
+
* carrying the redacted citations/evaluation and the `guardrails` summary.
|
|
28
|
+
* A strict-mode block yields only a done chunk with `success: false`.
|
|
29
|
+
*
|
|
24
30
|
* @param {object} input - Pipeline input
|
|
25
31
|
* @param {string} input.query - The user query
|
|
26
32
|
* @param {Float32Array} [input.queryVector] - Optional precomputed query vector
|
|
@@ -34,6 +40,28 @@ export class GuardrailsPipeline extends EventEmitter {
|
|
|
34
40
|
options?: object;
|
|
35
41
|
context?: object;
|
|
36
42
|
}): Promise<object>;
|
|
43
|
+
/**
|
|
44
|
+
* Whether the retrieval guard has a check that needs documents.
|
|
45
|
+
* @private
|
|
46
|
+
*/
|
|
47
|
+
private _retrievalChecksActive;
|
|
48
|
+
/**
|
|
49
|
+
* Milliseconds left of the caller's options.timeout, or undefined.
|
|
50
|
+
* @private
|
|
51
|
+
*/
|
|
52
|
+
private _remainingTimeout;
|
|
53
|
+
/** @private One warning per pipeline instance. */
|
|
54
|
+
private _warnRetrievalSkipped;
|
|
55
|
+
_warnedRetrievalSkipped: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Collect a token stream, run the post-generation guard on the whole
|
|
58
|
+
* answer and return an async iterable that yields the sanitized answer as
|
|
59
|
+
* one token chunk and then the done chunk. A blocked answer yields no
|
|
60
|
+
* tokens, only a done chunk with the error.
|
|
61
|
+
*
|
|
62
|
+
* @private
|
|
63
|
+
*/
|
|
64
|
+
private _releaseStream;
|
|
37
65
|
/**
|
|
38
66
|
* Citations and evaluation details copy sentences from the raw answer, so
|
|
39
67
|
* scrub every string in them with the same PII patterns as the answer.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export const NO_ACCESS: "rag:no-access";
|
|
1
2
|
/**
|
|
2
3
|
* The access fields (`acl`, `requiredRole`) set on a document's top level.
|
|
3
4
|
* Stores keep them on each result, as well as whatever metadata holds, so
|
|
@@ -6,3 +7,32 @@
|
|
|
6
7
|
* @returns {object}
|
|
7
8
|
*/
|
|
8
9
|
export function accessFields(doc: object): object;
|
|
10
|
+
/**
|
|
11
|
+
* The metadata to store for a document: its metadata, plus its top-level
|
|
12
|
+
* access fields under a reserved key when it has any.
|
|
13
|
+
* @param {object} doc
|
|
14
|
+
* @returns {object}
|
|
15
|
+
*/
|
|
16
|
+
export function storedMetadata(doc: object): object;
|
|
17
|
+
/**
|
|
18
|
+
* Splits stored metadata back into the document's metadata and its
|
|
19
|
+
* top-level access fields. Unreadable access fields restrict the document
|
|
20
|
+
* to no one.
|
|
21
|
+
* @param {object} stored
|
|
22
|
+
* @returns {{ metadata: object, access: object }}
|
|
23
|
+
*/
|
|
24
|
+
export function readStoredMetadata(stored: object): {
|
|
25
|
+
metadata: object;
|
|
26
|
+
access: object;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* One result for copies of the same document: the first copy's fields,
|
|
30
|
+
* restricted by every restriction any copy sets. Restrictions the first
|
|
31
|
+
* copy lacks go on its free top-level access fields; when they don't fit,
|
|
32
|
+
* its top-level restrictions and the new ones become a single list of the
|
|
33
|
+
* roles they all share, which is at least as strict.
|
|
34
|
+
* @param {object} first
|
|
35
|
+
* @param {object[]} others
|
|
36
|
+
* @returns {object}
|
|
37
|
+
*/
|
|
38
|
+
export function mergeAccess(first: object, others: object[]): object;
|
|
@@ -25,6 +25,25 @@ export class PluginSignatureVerifier {
|
|
|
25
25
|
* @returns {Promise<object>} Verification result
|
|
26
26
|
*/
|
|
27
27
|
verifyPluginSignature(manifest: object, signature: string, signerId: string, content?: object): Promise<object>;
|
|
28
|
+
/**
|
|
29
|
+
* Synchronous signature check shared by every verification path: input
|
|
30
|
+
* validation, trusted-signer lookup, Ed25519 signature over the canonical
|
|
31
|
+
* manifest, and the contentDigest check. Does not consult `enabled` and
|
|
32
|
+
* does not write audit entries; callers decide both.
|
|
33
|
+
*
|
|
34
|
+
* @param {object} manifest - Plugin manifest
|
|
35
|
+
* @param {string} signature - Base64 encoded signature
|
|
36
|
+
* @param {string} signerId - Signer identifier
|
|
37
|
+
* @param {object} [content] - `{ files, baseDir, contents }` to check
|
|
38
|
+
* against manifest.contentDigest (see computeContentDigest)
|
|
39
|
+
* @returns {{contentVerified: boolean, canonicalManifest: object}}
|
|
40
|
+
* contentVerified is false when the manifest has no contentDigest
|
|
41
|
+
* @throws {Error} When the manifest must not be trusted
|
|
42
|
+
*/
|
|
43
|
+
verifyManifestSync(manifest: object, signature: string, signerId: string, content?: object): {
|
|
44
|
+
contentVerified: boolean;
|
|
45
|
+
canonicalManifest: object;
|
|
46
|
+
};
|
|
28
47
|
/**
|
|
29
48
|
* Check the supplied plugin content against the signed contentDigest.
|
|
30
49
|
* @param {object} canonicalManifest - Signed manifest
|
|
@@ -42,6 +61,15 @@ export class PluginSignatureVerifier {
|
|
|
42
61
|
* @returns {Promise<boolean>} Verification result
|
|
43
62
|
*/
|
|
44
63
|
_verifyEd25519Signature(data: Buffer, signature: Buffer, publicKey: crypto.KeyObject | Buffer | string): Promise<boolean>;
|
|
64
|
+
/**
|
|
65
|
+
* Synchronous form of _verifyEd25519Signature.
|
|
66
|
+
* @param {Buffer} data - Data that was signed
|
|
67
|
+
* @param {Buffer} signature - Signature bytes
|
|
68
|
+
* @param {crypto.KeyObject|Buffer|string} publicKey - Ed25519 public key
|
|
69
|
+
* @returns {boolean}
|
|
70
|
+
* @private
|
|
71
|
+
*/
|
|
72
|
+
private _checkEd25519Signature;
|
|
45
73
|
/**
|
|
46
74
|
* Canonicalize manifest for consistent signing
|
|
47
75
|
* @param {object} manifest - Plugin manifest
|
|
@@ -125,6 +153,14 @@ export function computeContentDigest({ files, baseDir, contents }?: {
|
|
|
125
153
|
[x: string]: Buffer | string;
|
|
126
154
|
};
|
|
127
155
|
}): string;
|
|
156
|
+
/**
|
|
157
|
+
* Whether a manifest binds the plugin code: it has a contentDigest that is
|
|
158
|
+
* neither undefined nor null. Every path that decides "digest or no digest"
|
|
159
|
+
* (verification and the missing-digest warning) uses this one test.
|
|
160
|
+
* @param {object} manifest - Plugin manifest
|
|
161
|
+
* @returns {boolean}
|
|
162
|
+
*/
|
|
163
|
+
export function hasContentDigest(manifest: object): boolean;
|
|
128
164
|
/**
|
|
129
165
|
* Convert supported public key encodings to an Ed25519 KeyObject.
|
|
130
166
|
*
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrap a class so constructing it (or calling a static method) emits a
|
|
3
|
+
* one-time DeprecationWarning. A Proxy keeps the original's identity for
|
|
4
|
+
* prototype, name, statics, `extends` and `instanceof`, so instances created
|
|
5
|
+
* inside the library still satisfy `instanceof Wrapped`. The same class
|
|
6
|
+
* always gets the same wrapper, so aliases stay identical.
|
|
7
|
+
*
|
|
8
|
+
* @param {Function} Class - Class to wrap
|
|
9
|
+
* @param {object} info
|
|
10
|
+
* @param {string} info.name - Export name, used in the message and code
|
|
11
|
+
* @param {string} info.message - Full warning text
|
|
12
|
+
* @returns {Function} The wrapped class
|
|
13
|
+
*/
|
|
14
|
+
export function deprecateClass(Class: Function, { name, message }: {
|
|
15
|
+
name: string;
|
|
16
|
+
message: string;
|
|
17
|
+
}): Function;
|
|
18
|
+
/**
|
|
19
|
+
* Emit a DeprecationWarning once per process for each code.
|
|
20
|
+
* @param {string} code - Warning code, e.g. RAG_DEP_...
|
|
21
|
+
* @param {string} message - Full warning text
|
|
22
|
+
*/
|
|
23
|
+
export function warnOnce(code: string, message: string): void;
|