@pipeline-builder/api-core 3.3.34 → 3.4.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.
|
@@ -1,18 +1,4 @@
|
|
|
1
1
|
import type { Request, Response } from 'express';
|
|
2
|
-
/**
|
|
3
|
-
* Apply access control to a read-filter.
|
|
4
|
-
*
|
|
5
|
-
* Pass-through: forwards the caller's filter unchanged. Multi-tenant access
|
|
6
|
-
* scoping is handled in the query builder (`AccessControlQueryBuilder`),
|
|
7
|
-
* which combines the caller's `orgId` with `accessModifier` to return:
|
|
8
|
-
* caller's org rows + system-org public rows by default.
|
|
9
|
-
*
|
|
10
|
-
* Kept as a stable wrapper so route handlers retain a single, named hook
|
|
11
|
-
* for future per-route policy adjustments.
|
|
12
|
-
*/
|
|
13
|
-
export declare function applyAccessControl<T extends {
|
|
14
|
-
accessModifier?: string;
|
|
15
|
-
}>(filter: T, _req: Request): T;
|
|
16
2
|
/**
|
|
17
3
|
* Check whether a non-admin user may modify a public resource.
|
|
18
4
|
*
|
|
@@ -2,26 +2,11 @@
|
|
|
2
2
|
// Copyright 2026 Pipeline Builder Contributors
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.applyAccessControl = applyAccessControl;
|
|
6
5
|
exports.requirePublicAccess = requirePublicAccess;
|
|
7
6
|
const auth_1 = require("../middleware/auth");
|
|
8
7
|
const error_codes_1 = require("../types/error-codes");
|
|
9
8
|
const pipeline_1 = require("../types/pipeline");
|
|
10
9
|
const response_1 = require("../utils/response");
|
|
11
|
-
/**
|
|
12
|
-
* Apply access control to a read-filter.
|
|
13
|
-
*
|
|
14
|
-
* Pass-through: forwards the caller's filter unchanged. Multi-tenant access
|
|
15
|
-
* scoping is handled in the query builder (`AccessControlQueryBuilder`),
|
|
16
|
-
* which combines the caller's `orgId` with `accessModifier` to return:
|
|
17
|
-
* caller's org rows + system-org public rows by default.
|
|
18
|
-
*
|
|
19
|
-
* Kept as a stable wrapper so route handlers retain a single, named hook
|
|
20
|
-
* for future per-route policy adjustments.
|
|
21
|
-
*/
|
|
22
|
-
function applyAccessControl(filter, _req) {
|
|
23
|
-
return filter;
|
|
24
|
-
}
|
|
25
10
|
/**
|
|
26
11
|
* Check whether a non-admin user may modify a public resource.
|
|
27
12
|
*
|
|
@@ -46,4 +31,4 @@ function requirePublicAccess(req, res, resource) {
|
|
|
46
31
|
}
|
|
47
32
|
return true;
|
|
48
33
|
}
|
|
49
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
34
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNjZXNzLWhlbHBlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaGVscGVycy9hY2Nlc3MtaGVscGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsK0NBQStDO0FBQy9DLHNDQUFzQzs7QUF5QnRDLGtEQWVDO0FBckNELDZDQUFtRDtBQUNuRCxzREFBaUQ7QUFDakQsZ0RBQW1EO0FBQ25ELGdEQUE4QztBQUU5Qzs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUNILFNBQWdCLG1CQUFtQixDQUNqQyxHQUFZLEVBQ1osR0FBYSxFQUNiLFFBQXFDO0lBRXJDLElBQUksQ0FBQyxJQUFBLG9CQUFhLEVBQUMsR0FBRyxDQUFDLElBQUksUUFBUSxDQUFDLGNBQWMsS0FBSyx5QkFBYyxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQzlFLElBQUEsb0JBQVMsRUFDUCxHQUFHLEVBQ0gsR0FBRyxFQUNILGlEQUFpRCxFQUNqRCx1QkFBUyxDQUFDLHdCQUF3QixDQUNuQyxDQUFDO1FBQ0YsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBQ0QsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB0eXBlIHsgUmVxdWVzdCwgUmVzcG9uc2UgfSBmcm9tICdleHByZXNzJztcbmltcG9ydCB7IGlzU3lzdGVtQWRtaW4gfSBmcm9tICcuLi9taWRkbGV3YXJlL2F1dGgnO1xuaW1wb3J0IHsgRXJyb3JDb2RlIH0gZnJvbSAnLi4vdHlwZXMvZXJyb3ItY29kZXMnO1xuaW1wb3J0IHsgQWNjZXNzTW9kaWZpZXIgfSBmcm9tICcuLi90eXBlcy9waXBlbGluZSc7XG5pbXBvcnQgeyBzZW5kRXJyb3IgfSBmcm9tICcuLi91dGlscy9yZXNwb25zZSc7XG5cbi8qKlxuICogQ2hlY2sgd2hldGhlciBhIG5vbi1hZG1pbiB1c2VyIG1heSBtb2RpZnkgYSBwdWJsaWMgcmVzb3VyY2UuXG4gKlxuICogUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHJlcXVlc3QgbWF5IHByb2NlZWQuIFJldHVybnMgYGZhbHNlYCBhbmQgc2VuZHNcbiAqIGEgNDAzIHJlc3BvbnNlIGlmIHRoZSB1c2VyIGxhY2tzIHBlcm1pc3Npb24uXG4gKlxuICogQHBhcmFtIHJlcSAtIEV4cHJlc3MgcmVxdWVzdFxuICogQHBhcmFtIHJlcyAtIEV4cHJlc3MgcmVzcG9uc2VcbiAqIEBwYXJhbSByZXNvdXJjZSAtIFJlc291cmNlIHdpdGggYW4gYWNjZXNzTW9kaWZpZXIgZmllbGRcbiAqIEByZXR1cm5zIGB0cnVlYCBpZiBhY2Nlc3MgaXMgYWxsb3dlZCwgYGZhbHNlYCBpZiBibG9ja2VkIChyZXNwb25zZSBhbHJlYWR5IHNlbnQpXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGlmICghcmVxdWlyZVB1YmxpY0FjY2VzcyhyZXEsIHJlcywgcGlwZWxpbmUpKSByZXR1cm47XG4gKiBhd2FpdCBwaXBlbGluZVNlcnZpY2UuZGVsZXRlKGlkLCBvcmdJZCwgdXNlcklkKTtcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVxdWlyZVB1YmxpY0FjY2VzcyhcbiAgcmVxOiBSZXF1ZXN0LFxuICByZXM6IFJlc3BvbnNlLFxuICByZXNvdXJjZTogeyBhY2Nlc3NNb2RpZmllcj86IHN0cmluZyB9LFxuKTogYm9vbGVhbiB7XG4gIGlmICghaXNTeXN0ZW1BZG1pbihyZXEpICYmIHJlc291cmNlLmFjY2Vzc01vZGlmaWVyICE9PSBBY2Nlc3NNb2RpZmllci5QUklWQVRFKSB7XG4gICAgc2VuZEVycm9yKFxuICAgICAgcmVzLFxuICAgICAgNDAzLFxuICAgICAgJ09ubHkgc3lzdGVtIGFkbWlucyBjYW4gbW9kaWZ5IHB1YmxpYyByZXNvdXJjZXMuJyxcbiAgICAgIEVycm9yQ29kZS5JTlNVRkZJQ0lFTlRfUEVSTUlTU0lPTlMsXG4gICAgKTtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgcmV0dXJuIHRydWU7XG59XG4iXX0=
|
package/lib/services/index.d.ts
CHANGED
|
@@ -6,5 +6,4 @@ export * from './http-client';
|
|
|
6
6
|
export { DEFAULT_MAX_RETRIES, DEFAULT_RETRY_DELAY_MS, DEFAULT_MAX_RATE_LIMIT_RETRIES, calculateBackoff, isTransientStatusCode, isRateLimited, getRetryDecision, getErrorRetryDecision, } from './retry-strategy';
|
|
7
7
|
export type { RetryConfig, RetryDecision } from './retry-strategy';
|
|
8
8
|
export * from './quota';
|
|
9
|
-
export { logAdminAction, type AuditEntry } from './admin-audit';
|
|
10
9
|
export { enqueueComplianceEvent, registerComplianceQueueBackend, type ComplianceEvent } from './compliance-queue';
|
package/lib/services/index.js
CHANGED
|
@@ -16,7 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
17
17
|
};
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.registerComplianceQueueBackend = exports.enqueueComplianceEvent = exports.
|
|
19
|
+
exports.registerComplianceQueueBackend = exports.enqueueComplianceEvent = exports.getErrorRetryDecision = exports.getRetryDecision = exports.isRateLimited = exports.isTransientStatusCode = exports.calculateBackoff = exports.DEFAULT_MAX_RATE_LIMIT_RETRIES = exports.DEFAULT_RETRY_DELAY_MS = exports.DEFAULT_MAX_RETRIES = void 0;
|
|
20
20
|
__exportStar(require("./cache-service"), exports);
|
|
21
21
|
__exportStar(require("./compliance-client"), exports);
|
|
22
22
|
__exportStar(require("./compliance-event-subscriber"), exports);
|
|
@@ -32,9 +32,7 @@ Object.defineProperty(exports, "isRateLimited", { enumerable: true, get: functio
|
|
|
32
32
|
Object.defineProperty(exports, "getRetryDecision", { enumerable: true, get: function () { return retry_strategy_1.getRetryDecision; } });
|
|
33
33
|
Object.defineProperty(exports, "getErrorRetryDecision", { enumerable: true, get: function () { return retry_strategy_1.getErrorRetryDecision; } });
|
|
34
34
|
__exportStar(require("./quota"), exports);
|
|
35
|
-
var admin_audit_1 = require("./admin-audit");
|
|
36
|
-
Object.defineProperty(exports, "logAdminAction", { enumerable: true, get: function () { return admin_audit_1.logAdminAction; } });
|
|
37
35
|
var compliance_queue_1 = require("./compliance-queue");
|
|
38
36
|
Object.defineProperty(exports, "enqueueComplianceEvent", { enumerable: true, get: function () { return compliance_queue_1.enqueueComplianceEvent; } });
|
|
39
37
|
Object.defineProperty(exports, "registerComplianceQueueBackend", { enumerable: true, get: function () { return compliance_queue_1.registerComplianceQueueBackend; } });
|
|
40
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
38
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VydmljZXMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBRXRDLGtEQUFnQztBQUNoQyxzREFBb0M7QUFDcEMsZ0VBQThDO0FBQzlDLGtEQUFnQztBQUNoQyxnREFBOEI7QUFDOUIsbURBUzBCO0FBUnhCLHFIQUFBLG1CQUFtQixPQUFBO0FBQ25CLHdIQUFBLHNCQUFzQixPQUFBO0FBQ3RCLGdJQUFBLDhCQUE4QixPQUFBO0FBQzlCLGtIQUFBLGdCQUFnQixPQUFBO0FBQ2hCLHVIQUFBLHFCQUFxQixPQUFBO0FBQ3JCLCtHQUFBLGFBQWEsT0FBQTtBQUNiLGtIQUFBLGdCQUFnQixPQUFBO0FBQ2hCLHVIQUFBLHFCQUFxQixPQUFBO0FBR3ZCLDBDQUF3QjtBQUN4Qix1REFBa0g7QUFBekcsMEhBQUEsc0JBQXNCLE9BQUE7QUFBRSxrSUFBQSw4QkFBOEIsT0FBQSIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5leHBvcnQgKiBmcm9tICcuL2NhY2hlLXNlcnZpY2UnO1xuZXhwb3J0ICogZnJvbSAnLi9jb21wbGlhbmNlLWNsaWVudCc7XG5leHBvcnQgKiBmcm9tICcuL2NvbXBsaWFuY2UtZXZlbnQtc3Vic2NyaWJlcic7XG5leHBvcnQgKiBmcm9tICcuL2VudGl0eS1ldmVudHMnO1xuZXhwb3J0ICogZnJvbSAnLi9odHRwLWNsaWVudCc7XG5leHBvcnQge1xuICBERUZBVUxUX01BWF9SRVRSSUVTLFxuICBERUZBVUxUX1JFVFJZX0RFTEFZX01TLFxuICBERUZBVUxUX01BWF9SQVRFX0xJTUlUX1JFVFJJRVMsXG4gIGNhbGN1bGF0ZUJhY2tvZmYsXG4gIGlzVHJhbnNpZW50U3RhdHVzQ29kZSxcbiAgaXNSYXRlTGltaXRlZCxcbiAgZ2V0UmV0cnlEZWNpc2lvbixcbiAgZ2V0RXJyb3JSZXRyeURlY2lzaW9uLFxufSBmcm9tICcuL3JldHJ5LXN0cmF0ZWd5JztcbmV4cG9ydCB0eXBlIHsgUmV0cnlDb25maWcsIFJldHJ5RGVjaXNpb24gfSBmcm9tICcuL3JldHJ5LXN0cmF0ZWd5JztcbmV4cG9ydCAqIGZyb20gJy4vcXVvdGEnO1xuZXhwb3J0IHsgZW5xdWV1ZUNvbXBsaWFuY2VFdmVudCwgcmVnaXN0ZXJDb21wbGlhbmNlUXVldWVCYWNrZW5kLCB0eXBlIENvbXBsaWFuY2VFdmVudCB9IGZyb20gJy4vY29tcGxpYW5jZS1xdWV1ZSc7XG4iXX0=
|
package/package.json
CHANGED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export interface AuditEntry {
|
|
2
|
-
userId: string;
|
|
3
|
-
userEmail?: string;
|
|
4
|
-
orgId?: string;
|
|
5
|
-
action: string;
|
|
6
|
-
targetType: string;
|
|
7
|
-
targetId?: string;
|
|
8
|
-
targetName?: string;
|
|
9
|
-
detail?: Record<string, unknown>;
|
|
10
|
-
ipAddress?: string;
|
|
11
|
-
}
|
|
12
|
-
export declare function logAdminAction(entry: AuditEntry): void;
|
|
13
|
-
export declare function getAuditQueue(): AuditEntry[];
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Copyright 2026 Pipeline Builder Contributors
|
|
3
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.logAdminAction = logAdminAction;
|
|
6
|
-
exports.getAuditQueue = getAuditQueue;
|
|
7
|
-
const logger_1 = require("../utils/logger");
|
|
8
|
-
const logger = (0, logger_1.createLogger)('admin-audit');
|
|
9
|
-
const auditQueue = [];
|
|
10
|
-
let flushTimer = null;
|
|
11
|
-
function logAdminAction(entry) {
|
|
12
|
-
logger.info('Admin action', entry);
|
|
13
|
-
auditQueue.push(entry);
|
|
14
|
-
if (!flushTimer) {
|
|
15
|
-
flushTimer = setTimeout(flushAuditQueue, 5000);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
async function flushAuditQueue() {
|
|
19
|
-
flushTimer = null;
|
|
20
|
-
const batch = auditQueue.splice(0, auditQueue.length);
|
|
21
|
-
if (batch.length === 0)
|
|
22
|
-
return;
|
|
23
|
-
// Batch will be persisted when a DB writer is registered
|
|
24
|
-
for (const entry of batch) {
|
|
25
|
-
logger.debug('Audit entry queued', { action: entry.action, target: entry.targetType });
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
function getAuditQueue() {
|
|
29
|
-
return [...auditQueue];
|
|
30
|
-
}
|
|
31
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWRtaW4tYXVkaXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VydmljZXMvYWRtaW4tYXVkaXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7O0FBcUJ0Qyx3Q0FNQztBQVlELHNDQUVDO0FBdkNELDRDQUErQztBQUUvQyxNQUFNLE1BQU0sR0FBRyxJQUFBLHFCQUFZLEVBQUMsYUFBYSxDQUFDLENBQUM7QUFjM0MsTUFBTSxVQUFVLEdBQWlCLEVBQUUsQ0FBQztBQUNwQyxJQUFJLFVBQVUsR0FBeUMsSUFBSSxDQUFDO0FBRTVELFNBQWdCLGNBQWMsQ0FBQyxLQUFpQjtJQUM5QyxNQUFNLENBQUMsSUFBSSxDQUFDLGNBQWMsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUNuQyxVQUFVLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3ZCLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNoQixVQUFVLEdBQUcsVUFBVSxDQUFDLGVBQWUsRUFBRSxJQUFJLENBQUMsQ0FBQztJQUNqRCxDQUFDO0FBQ0gsQ0FBQztBQUVELEtBQUssVUFBVSxlQUFlO0lBQzVCLFVBQVUsR0FBRyxJQUFJLENBQUM7SUFDbEIsTUFBTSxLQUFLLEdBQUcsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ3RELElBQUksS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDO1FBQUUsT0FBTztJQUMvQix5REFBeUQ7SUFDekQsS0FBSyxNQUFNLEtBQUssSUFBSSxLQUFLLEVBQUUsQ0FBQztRQUMxQixNQUFNLENBQUMsS0FBSyxDQUFDLG9CQUFvQixFQUFFLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxVQUFVLEVBQUUsQ0FBQyxDQUFDO0lBQ3pGLENBQUM7QUFDSCxDQUFDO0FBRUQsU0FBZ0IsYUFBYTtJQUMzQixPQUFPLENBQUMsR0FBRyxVQUFVLENBQUMsQ0FBQztBQUN6QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciB9IGZyb20gJy4uL3V0aWxzL2xvZ2dlcic7XG5cbmNvbnN0IGxvZ2dlciA9IGNyZWF0ZUxvZ2dlcignYWRtaW4tYXVkaXQnKTtcblxuZXhwb3J0IGludGVyZmFjZSBBdWRpdEVudHJ5IHtcbiAgdXNlcklkOiBzdHJpbmc7XG4gIHVzZXJFbWFpbD86IHN0cmluZztcbiAgb3JnSWQ/OiBzdHJpbmc7XG4gIGFjdGlvbjogc3RyaW5nO1xuICB0YXJnZXRUeXBlOiBzdHJpbmc7XG4gIHRhcmdldElkPzogc3RyaW5nO1xuICB0YXJnZXROYW1lPzogc3RyaW5nO1xuICBkZXRhaWw/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgaXBBZGRyZXNzPzogc3RyaW5nO1xufVxuXG5jb25zdCBhdWRpdFF1ZXVlOiBBdWRpdEVudHJ5W10gPSBbXTtcbmxldCBmbHVzaFRpbWVyOiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0PiB8IG51bGwgPSBudWxsO1xuXG5leHBvcnQgZnVuY3Rpb24gbG9nQWRtaW5BY3Rpb24oZW50cnk6IEF1ZGl0RW50cnkpOiB2b2lkIHtcbiAgbG9nZ2VyLmluZm8oJ0FkbWluIGFjdGlvbicsIGVudHJ5KTtcbiAgYXVkaXRRdWV1ZS5wdXNoKGVudHJ5KTtcbiAgaWYgKCFmbHVzaFRpbWVyKSB7XG4gICAgZmx1c2hUaW1lciA9IHNldFRpbWVvdXQoZmx1c2hBdWRpdFF1ZXVlLCA1MDAwKTtcbiAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiBmbHVzaEF1ZGl0UXVldWUoKTogUHJvbWlzZTx2b2lkPiB7XG4gIGZsdXNoVGltZXIgPSBudWxsO1xuICBjb25zdCBiYXRjaCA9IGF1ZGl0UXVldWUuc3BsaWNlKDAsIGF1ZGl0UXVldWUubGVuZ3RoKTtcbiAgaWYgKGJhdGNoLmxlbmd0aCA9PT0gMCkgcmV0dXJuO1xuICAvLyBCYXRjaCB3aWxsIGJlIHBlcnNpc3RlZCB3aGVuIGEgREIgd3JpdGVyIGlzIHJlZ2lzdGVyZWRcbiAgZm9yIChjb25zdCBlbnRyeSBvZiBiYXRjaCkge1xuICAgIGxvZ2dlci5kZWJ1ZygnQXVkaXQgZW50cnkgcXVldWVkJywgeyBhY3Rpb246IGVudHJ5LmFjdGlvbiwgdGFyZ2V0OiBlbnRyeS50YXJnZXRUeXBlIH0pO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRBdWRpdFF1ZXVlKCk6IEF1ZGl0RW50cnlbXSB7XG4gIHJldHVybiBbLi4uYXVkaXRRdWV1ZV07XG59XG4iXX0=
|