@pipeline-builder/pipeline-data 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +34 -0
- package/drizzle.config.ts +17 -0
- package/lib/api/access-control-builder.d.ts +109 -0
- package/lib/api/access-control-builder.js +181 -0
- package/lib/api/crud-service.d.ts +170 -0
- package/lib/api/crud-service.js +387 -0
- package/lib/api/query-builders.d.ts +74 -0
- package/lib/api/query-builders.js +336 -0
- package/lib/api/reporting-service.d.ts +131 -0
- package/lib/api/reporting-service.js +248 -0
- package/lib/core/query-filters.d.ts +235 -0
- package/lib/core/query-filters.js +23 -0
- package/lib/database/drizzle-schema.d.ts +10043 -0
- package/lib/database/drizzle-schema.js +715 -0
- package/lib/database/index.d.ts +3 -0
- package/lib/database/index.js +22 -0
- package/lib/database/postgres-connection.d.ts +232 -0
- package/lib/database/postgres-connection.js +456 -0
- package/lib/database/retry-strategy.d.ts +68 -0
- package/lib/database/retry-strategy.js +126 -0
- package/lib/index.d.ts +30 -0
- package/lib/index.js +52 -0
- package/package.json +125 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { SQL } from 'drizzle-orm';
|
|
2
|
+
import type { AnyColumn } from 'drizzle-orm/column';
|
|
3
|
+
import type { PgTable } from 'drizzle-orm/pg-core';
|
|
4
|
+
/**
|
|
5
|
+
* Cast Drizzle query results to a typed array.
|
|
6
|
+
* Drizzle's generic return type (`PgSelectBase<...>`) doesn't narrow to our
|
|
7
|
+
* entity generics, so an explicit cast is needed. Centralised here so every
|
|
8
|
+
* call-site stays one-liner clean and the cast is documented in one place.
|
|
9
|
+
*/
|
|
10
|
+
export declare function drizzleRows<T>(rows: unknown): T[];
|
|
11
|
+
/** Cast a Drizzle aggregate result to extract `[{ count: number }]`. */
|
|
12
|
+
export declare function drizzleCount(rows: unknown): [{
|
|
13
|
+
count: number;
|
|
14
|
+
}];
|
|
15
|
+
/**
|
|
16
|
+
* Base interface for entities with common fields
|
|
17
|
+
*/
|
|
18
|
+
export interface BaseEntity {
|
|
19
|
+
id: string;
|
|
20
|
+
orgId: string;
|
|
21
|
+
isDefault: boolean;
|
|
22
|
+
createdAt: Date;
|
|
23
|
+
updatedAt: Date;
|
|
24
|
+
createdBy: string;
|
|
25
|
+
updatedBy: string;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Pagination and sorting options
|
|
30
|
+
*/
|
|
31
|
+
export interface QueryOptions {
|
|
32
|
+
limit?: number;
|
|
33
|
+
offset?: number;
|
|
34
|
+
sortBy?: string;
|
|
35
|
+
sortOrder?: 'asc' | 'desc';
|
|
36
|
+
/** When true, runs a separate COUNT(*) query to include exact total. Default: false. */
|
|
37
|
+
includeTotal?: boolean;
|
|
38
|
+
/** Cursor-based pagination: fetch rows after this cursor value (uses sortBy column). */
|
|
39
|
+
cursor?: string;
|
|
40
|
+
/** Sparse fieldset: column names to select. Returns all columns when omitted. */
|
|
41
|
+
fields?: string[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Paginated result with metadata
|
|
45
|
+
*/
|
|
46
|
+
export interface PaginatedResult<T> {
|
|
47
|
+
data: T[];
|
|
48
|
+
/** Total count of matching entities. Only present when includeTotal is true. */
|
|
49
|
+
total?: number;
|
|
50
|
+
limit: number;
|
|
51
|
+
offset: number;
|
|
52
|
+
hasMore: boolean;
|
|
53
|
+
/** Cursor pointing to the last item, for cursor-based pagination. */
|
|
54
|
+
nextCursor?: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Abstract CRUD service with access control and common operations.
|
|
58
|
+
*
|
|
59
|
+
* @typeParam TEntity - Entity type extending BaseEntity
|
|
60
|
+
* @typeParam TFilter - Filter type for query parameters
|
|
61
|
+
* @typeParam TInsert - Insert DTO type
|
|
62
|
+
* @typeParam TUpdate - Update DTO type
|
|
63
|
+
*
|
|
64
|
+
* Type assertions (`as any`, `as unknown as T`) are used throughout for Drizzle ORM compatibility.
|
|
65
|
+
* This is safe because: access control filters by orgId, schema validation is at the DB level,
|
|
66
|
+
* and each subclass is tested for type correctness.
|
|
67
|
+
*
|
|
68
|
+
* Errors are not caught here — they propagate to the route-level error handler (`withRoute`)
|
|
69
|
+
* which provides consistent logging with request context.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* class PipelineService extends CrudService<Pipeline, PipelineFilter, PipelineInsert, PipelineUpdate> {
|
|
74
|
+
* protected get schema() { return schema.pipeline; }
|
|
75
|
+
* protected buildConditions(filter, orgId) { return buildPipelineConditions(filter, orgId); }
|
|
76
|
+
* protected getSortColumn(sortBy) { return sortColumnMap[sortBy] ?? null; }
|
|
77
|
+
* protected getProjectColumn() { return schema.pipeline.project; }
|
|
78
|
+
* protected getOrgColumn() { return schema.pipeline.organization; }
|
|
79
|
+
* }
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export declare abstract class CrudService<TEntity extends BaseEntity, TFilter, TInsert, TUpdate> {
|
|
83
|
+
/** Drizzle schema table for this entity */
|
|
84
|
+
protected abstract get schema(): PgTable;
|
|
85
|
+
/** Build SQL conditions for filtering entities */
|
|
86
|
+
protected abstract buildConditions(filter: Partial<TFilter>, orgId?: string): SQL[];
|
|
87
|
+
/** Get the schema column for sorting by field name */
|
|
88
|
+
protected abstract getSortColumn(sortBy: string): AnyColumn | null;
|
|
89
|
+
/** Get the project column for setDefault scoping (null if entity has no project scope) */
|
|
90
|
+
protected abstract getProjectColumn(): AnyColumn | null;
|
|
91
|
+
/** Get the organization column for setDefault scoping */
|
|
92
|
+
protected abstract getOrgColumn(): AnyColumn;
|
|
93
|
+
/** Get the unique constraint columns for onConflictDoUpdate */
|
|
94
|
+
protected abstract get conflictTarget(): AnyColumn[];
|
|
95
|
+
private readonly _logger;
|
|
96
|
+
/** Build conditions for a single entity by ID. */
|
|
97
|
+
private idConditions;
|
|
98
|
+
/** Called after a new entity is created */
|
|
99
|
+
protected onAfterCreate(_entity: TEntity, _userId: string): Promise<void>;
|
|
100
|
+
/** Called after an entity is updated */
|
|
101
|
+
protected onAfterUpdate(_id: string, _entity: TEntity, _userId: string): Promise<void>;
|
|
102
|
+
/** Called after an entity is soft-deleted */
|
|
103
|
+
protected onAfterDelete(_id: string, _entity: TEntity, _userId: string): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Find entities matching filter criteria
|
|
106
|
+
*
|
|
107
|
+
* @param filter - Filter criteria
|
|
108
|
+
* @param orgId - User's organization ID (optional — omit for anonymous/system-public-only access)
|
|
109
|
+
*/
|
|
110
|
+
find(filter: Partial<TFilter>, orgId?: string): Promise<TEntity[]>;
|
|
111
|
+
/**
|
|
112
|
+
* Find entities with pagination and sorting
|
|
113
|
+
*
|
|
114
|
+
* @param filter - Filter criteria
|
|
115
|
+
* @param orgId - User's organization ID (optional — omit for anonymous/system-public-only access)
|
|
116
|
+
* @param options - Pagination and sorting options
|
|
117
|
+
*/
|
|
118
|
+
findPaginated(filter: Partial<TFilter>, orgId?: string, options?: QueryOptions): Promise<PaginatedResult<TEntity>>;
|
|
119
|
+
/**
|
|
120
|
+
* Build a column selection map for sparse fieldsets.
|
|
121
|
+
* Falls back to full select if no matching columns found.
|
|
122
|
+
*/
|
|
123
|
+
private buildFieldSelect;
|
|
124
|
+
/**
|
|
125
|
+
* Count entities matching filter criteria
|
|
126
|
+
*
|
|
127
|
+
* @param filter - Filter criteria
|
|
128
|
+
* @param orgId - User's organization ID (optional — omit for anonymous/system-public-only access)
|
|
129
|
+
*/
|
|
130
|
+
count(filter: Partial<TFilter>, orgId?: string): Promise<number>;
|
|
131
|
+
/**
|
|
132
|
+
* Find a single entity by ID
|
|
133
|
+
*
|
|
134
|
+
* @param id - Entity ID
|
|
135
|
+
* @param orgId - User's organization ID (optional — omit for anonymous/system-public-only access)
|
|
136
|
+
*/
|
|
137
|
+
findById(id: string, orgId?: string): Promise<TEntity | null>;
|
|
138
|
+
/**
|
|
139
|
+
* Create a new entity
|
|
140
|
+
*/
|
|
141
|
+
create(data: TInsert, userId: string): Promise<TEntity>;
|
|
142
|
+
/**
|
|
143
|
+
* Update an existing entity
|
|
144
|
+
*/
|
|
145
|
+
update(id: string, data: Partial<TUpdate>, orgId: string, userId: string): Promise<TEntity | null>;
|
|
146
|
+
/**
|
|
147
|
+
* Delete an entity (soft delete by setting isActive = false)
|
|
148
|
+
*/
|
|
149
|
+
delete(id: string, orgId: string, userId: string): Promise<TEntity | null>;
|
|
150
|
+
/**
|
|
151
|
+
* Set an entity as the default for a project/organization scope.
|
|
152
|
+
* Marks all other entities as non-default, then sets the specified entity.
|
|
153
|
+
* Uses a transaction to ensure atomicity.
|
|
154
|
+
*/
|
|
155
|
+
setDefault(project: string, org: string, id: string, userId: string): Promise<TEntity>;
|
|
156
|
+
/**
|
|
157
|
+
* Update multiple entities matching filter
|
|
158
|
+
*/
|
|
159
|
+
updateMany(filter: Partial<TFilter>, data: Partial<TUpdate>, orgId: string, userId: string): Promise<TEntity[]>;
|
|
160
|
+
/**
|
|
161
|
+
* Create multiple entities in a single batch insert.
|
|
162
|
+
* Uses upsert (onConflictDoUpdate) — all rows are inserted in one query per chunk.
|
|
163
|
+
* Chunks of 100 to stay within PostgreSQL parameter limits.
|
|
164
|
+
*/
|
|
165
|
+
bulkCreate(items: TInsert[], userId: string): Promise<TEntity[]>;
|
|
166
|
+
/**
|
|
167
|
+
* Soft-delete multiple entities by IDs in a single batch operation.
|
|
168
|
+
*/
|
|
169
|
+
bulkDelete(ids: string[], orgId: string, userId: string): Promise<TEntity[]>;
|
|
170
|
+
}
|
|
@@ -0,0 +1,387 @@
|
|
|
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.CrudService = void 0;
|
|
6
|
+
exports.drizzleRows = drizzleRows;
|
|
7
|
+
exports.drizzleCount = drizzleCount;
|
|
8
|
+
const api_core_1 = require("@pipeline-builder/api-core");
|
|
9
|
+
const drizzle_orm_1 = require("drizzle-orm");
|
|
10
|
+
const postgres_connection_1 = require("../database/postgres-connection");
|
|
11
|
+
/** Pagination defaults — read from env to match CoreConstants in pipeline-core. */
|
|
12
|
+
const DEFAULT_PAGE_LIMIT = parseInt(process.env.DEFAULT_PAGE_LIMIT || '100', 10);
|
|
13
|
+
const MAX_PAGE_LIMIT = parseInt(process.env.MAX_PAGE_LIMIT || '1000', 10);
|
|
14
|
+
/**
|
|
15
|
+
* Cast Drizzle query results to a typed array.
|
|
16
|
+
* Drizzle's generic return type (`PgSelectBase<...>`) doesn't narrow to our
|
|
17
|
+
* entity generics, so an explicit cast is needed. Centralised here so every
|
|
18
|
+
* call-site stays one-liner clean and the cast is documented in one place.
|
|
19
|
+
*/
|
|
20
|
+
function drizzleRows(rows) {
|
|
21
|
+
return rows;
|
|
22
|
+
}
|
|
23
|
+
/** Cast a Drizzle aggregate result to extract `[{ count: number }]`. */
|
|
24
|
+
function drizzleCount(rows) {
|
|
25
|
+
return rows;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Abstract CRUD service with access control and common operations.
|
|
29
|
+
*
|
|
30
|
+
* @typeParam TEntity - Entity type extending BaseEntity
|
|
31
|
+
* @typeParam TFilter - Filter type for query parameters
|
|
32
|
+
* @typeParam TInsert - Insert DTO type
|
|
33
|
+
* @typeParam TUpdate - Update DTO type
|
|
34
|
+
*
|
|
35
|
+
* Type assertions (`as any`, `as unknown as T`) are used throughout for Drizzle ORM compatibility.
|
|
36
|
+
* This is safe because: access control filters by orgId, schema validation is at the DB level,
|
|
37
|
+
* and each subclass is tested for type correctness.
|
|
38
|
+
*
|
|
39
|
+
* Errors are not caught here — they propagate to the route-level error handler (`withRoute`)
|
|
40
|
+
* which provides consistent logging with request context.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* class PipelineService extends CrudService<Pipeline, PipelineFilter, PipelineInsert, PipelineUpdate> {
|
|
45
|
+
* protected get schema() { return schema.pipeline; }
|
|
46
|
+
* protected buildConditions(filter, orgId) { return buildPipelineConditions(filter, orgId); }
|
|
47
|
+
* protected getSortColumn(sortBy) { return sortColumnMap[sortBy] ?? null; }
|
|
48
|
+
* protected getProjectColumn() { return schema.pipeline.project; }
|
|
49
|
+
* protected getOrgColumn() { return schema.pipeline.organization; }
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
class CrudService {
|
|
54
|
+
_logger = (0, api_core_1.createLogger)('CrudService');
|
|
55
|
+
/** Build conditions for a single entity by ID. */
|
|
56
|
+
idConditions(id, orgId) {
|
|
57
|
+
return this.buildConditions({ id }, orgId);
|
|
58
|
+
}
|
|
59
|
+
// Lifecycle hooks — override in subclasses to react to mutations
|
|
60
|
+
// These are fire-and-forget: errors are logged but never block the caller.
|
|
61
|
+
/** Called after a new entity is created */
|
|
62
|
+
async onAfterCreate(_entity, _userId) { }
|
|
63
|
+
/** Called after an entity is updated */
|
|
64
|
+
async onAfterUpdate(_id, _entity, _userId) { }
|
|
65
|
+
/** Called after an entity is soft-deleted */
|
|
66
|
+
async onAfterDelete(_id, _entity, _userId) { }
|
|
67
|
+
/**
|
|
68
|
+
* Find entities matching filter criteria
|
|
69
|
+
*
|
|
70
|
+
* @param filter - Filter criteria
|
|
71
|
+
* @param orgId - User's organization ID (optional — omit for anonymous/system-public-only access)
|
|
72
|
+
*/
|
|
73
|
+
async find(filter, orgId) {
|
|
74
|
+
const conditions = this.buildConditions(filter, orgId);
|
|
75
|
+
return postgres_connection_1.db
|
|
76
|
+
.select()
|
|
77
|
+
.from(this.schema)
|
|
78
|
+
.where((0, drizzle_orm_1.and)(...conditions)).then(r => drizzleRows(r));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Find entities with pagination and sorting
|
|
82
|
+
*
|
|
83
|
+
* @param filter - Filter criteria
|
|
84
|
+
* @param orgId - User's organization ID (optional — omit for anonymous/system-public-only access)
|
|
85
|
+
* @param options - Pagination and sorting options
|
|
86
|
+
*/
|
|
87
|
+
async findPaginated(filter, orgId, options = {}) {
|
|
88
|
+
const { limit: rawLimit = DEFAULT_PAGE_LIMIT, offset = 0, sortBy, sortOrder = 'asc', includeTotal = false, cursor, fields } = options;
|
|
89
|
+
const limit = Math.min(Math.max(1, rawLimit), MAX_PAGE_LIMIT);
|
|
90
|
+
// Cursor and offset are mutually exclusive — cursor takes precedence
|
|
91
|
+
const useCursor = !!(cursor && sortBy);
|
|
92
|
+
const conditions = this.buildConditions(filter, orgId);
|
|
93
|
+
// Cursor-based pagination: add WHERE clause for keyset pagination
|
|
94
|
+
if (useCursor) {
|
|
95
|
+
const sortColumn = this.getSortColumn(sortBy);
|
|
96
|
+
if (sortColumn) {
|
|
97
|
+
const op = sortOrder === 'desc'
|
|
98
|
+
? (0, drizzle_orm_1.sql) `${sortColumn} < ${cursor}`
|
|
99
|
+
: (0, drizzle_orm_1.sql) `${sortColumn} > ${cursor}`;
|
|
100
|
+
conditions.push(op);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Build SELECT — sparse fieldset when fields are specified
|
|
104
|
+
const selectSpec = fields ? this.buildFieldSelect(fields) : undefined;
|
|
105
|
+
let query = selectSpec
|
|
106
|
+
? postgres_connection_1.db.select(selectSpec).from(this.schema).where((0, drizzle_orm_1.and)(...conditions))
|
|
107
|
+
: postgres_connection_1.db.select().from(this.schema).where((0, drizzle_orm_1.and)(...conditions));
|
|
108
|
+
if (sortBy) {
|
|
109
|
+
const sortColumn = this.getSortColumn(sortBy);
|
|
110
|
+
if (sortColumn) {
|
|
111
|
+
query = query.orderBy(sortOrder === 'desc' ? (0, drizzle_orm_1.desc)(sortColumn) : (0, drizzle_orm_1.asc)(sortColumn));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Fetch limit+1 to detect hasMore without COUNT(*)
|
|
115
|
+
const effectiveOffset = useCursor ? 0 : offset;
|
|
116
|
+
const rows = await query
|
|
117
|
+
.limit(limit + 1)
|
|
118
|
+
.offset(effectiveOffset).then(r => drizzleRows(r));
|
|
119
|
+
const hasMore = rows.length > limit;
|
|
120
|
+
const data = hasMore ? rows.slice(0, limit) : rows;
|
|
121
|
+
const result = { data, limit, offset: effectiveOffset, hasMore };
|
|
122
|
+
// Provide next cursor from last item's sort column value
|
|
123
|
+
if (data.length > 0 && sortBy) {
|
|
124
|
+
const lastItem = data[data.length - 1];
|
|
125
|
+
const cursorValue = lastItem[sortBy];
|
|
126
|
+
if (cursorValue !== undefined) {
|
|
127
|
+
result.nextCursor = cursorValue instanceof Date ? cursorValue.toISOString() : String(cursorValue);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Only run the COUNT(*) query when the caller explicitly needs the total
|
|
131
|
+
if (includeTotal) {
|
|
132
|
+
const baseConditions = this.buildConditions(filter, orgId);
|
|
133
|
+
const [countResult] = await postgres_connection_1.db
|
|
134
|
+
.select({ count: (0, drizzle_orm_1.sql) `count(*)::int` })
|
|
135
|
+
.from(this.schema)
|
|
136
|
+
.where((0, drizzle_orm_1.and)(...baseConditions)).then(r => drizzleCount(r));
|
|
137
|
+
result.total = countResult?.count || 0;
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Build a column selection map for sparse fieldsets.
|
|
143
|
+
* Falls back to full select if no matching columns found.
|
|
144
|
+
*/
|
|
145
|
+
buildFieldSelect(fields) {
|
|
146
|
+
if (fields.length === 0)
|
|
147
|
+
return undefined;
|
|
148
|
+
const columns = {};
|
|
149
|
+
// Always include id for entity identity
|
|
150
|
+
columns.id = this.schema.id;
|
|
151
|
+
for (const field of fields) {
|
|
152
|
+
if (field === 'id')
|
|
153
|
+
continue; // Already included
|
|
154
|
+
const col = this.schema[field];
|
|
155
|
+
if (col)
|
|
156
|
+
columns[field] = col;
|
|
157
|
+
}
|
|
158
|
+
// At minimum we'll have { id }, which is valid
|
|
159
|
+
return columns;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Count entities matching filter criteria
|
|
163
|
+
*
|
|
164
|
+
* @param filter - Filter criteria
|
|
165
|
+
* @param orgId - User's organization ID (optional — omit for anonymous/system-public-only access)
|
|
166
|
+
*/
|
|
167
|
+
async count(filter, orgId) {
|
|
168
|
+
const conditions = this.buildConditions(filter, orgId);
|
|
169
|
+
const [result] = await postgres_connection_1.db
|
|
170
|
+
.select({ count: (0, drizzle_orm_1.sql) `count(*)::int` })
|
|
171
|
+
.from(this.schema)
|
|
172
|
+
.where((0, drizzle_orm_1.and)(...conditions)).then(r => drizzleCount(r));
|
|
173
|
+
return result?.count || 0;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Find a single entity by ID
|
|
177
|
+
*
|
|
178
|
+
* @param id - Entity ID
|
|
179
|
+
* @param orgId - User's organization ID (optional — omit for anonymous/system-public-only access)
|
|
180
|
+
*/
|
|
181
|
+
async findById(id, orgId) {
|
|
182
|
+
const conditions = this.idConditions(id, orgId);
|
|
183
|
+
const results = await postgres_connection_1.db
|
|
184
|
+
.select()
|
|
185
|
+
.from(this.schema)
|
|
186
|
+
.where((0, drizzle_orm_1.and)(...conditions))
|
|
187
|
+
.limit(1).then(r => drizzleRows(r));
|
|
188
|
+
return results[0] || null;
|
|
189
|
+
}
|
|
190
|
+
// Mutation operations
|
|
191
|
+
/**
|
|
192
|
+
* Create a new entity
|
|
193
|
+
*/
|
|
194
|
+
async create(data, userId) {
|
|
195
|
+
const [created] = await postgres_connection_1.db
|
|
196
|
+
.insert(this.schema)
|
|
197
|
+
.values({
|
|
198
|
+
...data,
|
|
199
|
+
createdBy: userId || 'system',
|
|
200
|
+
updatedBy: userId || 'system',
|
|
201
|
+
})
|
|
202
|
+
.onConflictDoUpdate({
|
|
203
|
+
target: this.conflictTarget,
|
|
204
|
+
set: {
|
|
205
|
+
...data,
|
|
206
|
+
updatedAt: new Date(),
|
|
207
|
+
updatedBy: userId || 'system',
|
|
208
|
+
},
|
|
209
|
+
})
|
|
210
|
+
.returning().then(r => drizzleRows(r));
|
|
211
|
+
this.onAfterCreate(created, userId).catch(err => this._logger.warn('Lifecycle hook failed', { error: String(err) }));
|
|
212
|
+
return created;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Update an existing entity
|
|
216
|
+
*/
|
|
217
|
+
async update(id, data, orgId, userId) {
|
|
218
|
+
const conditions = this.idConditions(id, orgId);
|
|
219
|
+
const [updated] = await postgres_connection_1.db
|
|
220
|
+
.update(this.schema)
|
|
221
|
+
.set({
|
|
222
|
+
...data,
|
|
223
|
+
updatedAt: new Date(),
|
|
224
|
+
updatedBy: userId || 'system',
|
|
225
|
+
})
|
|
226
|
+
.where((0, drizzle_orm_1.and)(...conditions))
|
|
227
|
+
.returning().then(r => drizzleRows(r));
|
|
228
|
+
if (updated) {
|
|
229
|
+
this.onAfterUpdate(id, updated, userId).catch(err => this._logger.warn('Lifecycle hook failed', { error: String(err) }));
|
|
230
|
+
}
|
|
231
|
+
return updated || null;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Delete an entity (soft delete by setting isActive = false)
|
|
235
|
+
*/
|
|
236
|
+
async delete(id, orgId, userId) {
|
|
237
|
+
const conditions = this.idConditions(id, orgId);
|
|
238
|
+
const [deleted] = await postgres_connection_1.db
|
|
239
|
+
.update(this.schema)
|
|
240
|
+
.set({
|
|
241
|
+
isActive: false,
|
|
242
|
+
updatedAt: new Date(),
|
|
243
|
+
updatedBy: userId || 'system',
|
|
244
|
+
deletedAt: new Date(),
|
|
245
|
+
deletedBy: userId || 'system',
|
|
246
|
+
})
|
|
247
|
+
.where((0, drizzle_orm_1.and)(...conditions))
|
|
248
|
+
.returning().then(r => drizzleRows(r));
|
|
249
|
+
if (deleted) {
|
|
250
|
+
this.onAfterDelete(id, deleted, userId).catch(err => this._logger.warn('Lifecycle hook failed', { error: String(err) }));
|
|
251
|
+
}
|
|
252
|
+
return deleted || null;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Set an entity as the default for a project/organization scope.
|
|
256
|
+
* Marks all other entities as non-default, then sets the specified entity.
|
|
257
|
+
* Uses a transaction to ensure atomicity.
|
|
258
|
+
*/
|
|
259
|
+
async setDefault(project, org, id, userId) {
|
|
260
|
+
return postgres_connection_1.db.transaction(async (tx) => {
|
|
261
|
+
const orgColumn = this.getOrgColumn();
|
|
262
|
+
const projectColumn = this.getProjectColumn();
|
|
263
|
+
// Build scoping conditions for clearing defaults
|
|
264
|
+
const scopeConditions = [
|
|
265
|
+
(0, drizzle_orm_1.eq)(orgColumn, org),
|
|
266
|
+
(0, drizzle_orm_1.eq)(this.schema.isDefault, true),
|
|
267
|
+
];
|
|
268
|
+
if (projectColumn) {
|
|
269
|
+
scopeConditions.push((0, drizzle_orm_1.eq)(projectColumn, project));
|
|
270
|
+
}
|
|
271
|
+
// Lock existing defaults with FOR UPDATE to prevent concurrent setDefault races
|
|
272
|
+
await tx.execute((0, drizzle_orm_1.sql) `SELECT id FROM ${this.schema}
|
|
273
|
+
WHERE ${orgColumn} = ${org}
|
|
274
|
+
AND ${this.schema.isDefault} = true
|
|
275
|
+
${projectColumn ? (0, drizzle_orm_1.sql) `AND ${projectColumn} = ${project}` : (0, drizzle_orm_1.sql) ``}
|
|
276
|
+
FOR UPDATE`);
|
|
277
|
+
// Mark all entities in scope as non-default
|
|
278
|
+
await tx
|
|
279
|
+
.update(this.schema)
|
|
280
|
+
.set({
|
|
281
|
+
isDefault: false,
|
|
282
|
+
updatedAt: new Date(),
|
|
283
|
+
updatedBy: userId || 'system',
|
|
284
|
+
})
|
|
285
|
+
.where((0, drizzle_orm_1.and)(...scopeConditions));
|
|
286
|
+
// Set the specified entity as default
|
|
287
|
+
const [updated] = await tx
|
|
288
|
+
.update(this.schema)
|
|
289
|
+
.set({
|
|
290
|
+
isDefault: true,
|
|
291
|
+
updatedAt: new Date(),
|
|
292
|
+
updatedBy: userId || 'system',
|
|
293
|
+
})
|
|
294
|
+
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(this.schema.id, id), (0, drizzle_orm_1.eq)(orgColumn, org)))
|
|
295
|
+
.returning().then(r => drizzleRows(r));
|
|
296
|
+
if (!updated) {
|
|
297
|
+
throw new api_core_1.NotFoundError(`Entity with id ${id} not found`);
|
|
298
|
+
}
|
|
299
|
+
return updated;
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Update multiple entities matching filter
|
|
304
|
+
*/
|
|
305
|
+
async updateMany(filter, data, orgId, userId) {
|
|
306
|
+
const conditions = this.buildConditions(filter, orgId);
|
|
307
|
+
return postgres_connection_1.db
|
|
308
|
+
.update(this.schema)
|
|
309
|
+
.set({
|
|
310
|
+
...data,
|
|
311
|
+
updatedAt: new Date(),
|
|
312
|
+
updatedBy: userId || 'system',
|
|
313
|
+
})
|
|
314
|
+
.where((0, drizzle_orm_1.and)(...conditions))
|
|
315
|
+
.returning().then(r => drizzleRows(r));
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Create multiple entities in a single batch insert.
|
|
319
|
+
* Uses upsert (onConflictDoUpdate) — all rows are inserted in one query per chunk.
|
|
320
|
+
* Chunks of 100 to stay within PostgreSQL parameter limits.
|
|
321
|
+
*/
|
|
322
|
+
async bulkCreate(items, userId) {
|
|
323
|
+
if (items.length === 0)
|
|
324
|
+
return [];
|
|
325
|
+
const CHUNK_SIZE = 100;
|
|
326
|
+
const now = new Date();
|
|
327
|
+
const user = userId || 'system';
|
|
328
|
+
const results = await postgres_connection_1.db.transaction(async (tx) => {
|
|
329
|
+
const allCreated = [];
|
|
330
|
+
for (let i = 0; i < items.length; i += CHUNK_SIZE) {
|
|
331
|
+
const chunk = items.slice(i, i + CHUNK_SIZE);
|
|
332
|
+
const values = chunk.map(data => ({
|
|
333
|
+
...data,
|
|
334
|
+
createdBy: user,
|
|
335
|
+
updatedBy: user,
|
|
336
|
+
}));
|
|
337
|
+
const created = await tx
|
|
338
|
+
.insert(this.schema)
|
|
339
|
+
.values(values)
|
|
340
|
+
.onConflictDoUpdate({
|
|
341
|
+
target: this.conflictTarget,
|
|
342
|
+
set: {
|
|
343
|
+
updatedAt: now,
|
|
344
|
+
updatedBy: user,
|
|
345
|
+
},
|
|
346
|
+
})
|
|
347
|
+
.returning().then(r => drizzleRows(r));
|
|
348
|
+
allCreated.push(...created);
|
|
349
|
+
}
|
|
350
|
+
return allCreated;
|
|
351
|
+
});
|
|
352
|
+
for (const entity of results) {
|
|
353
|
+
this.onAfterCreate(entity, userId).catch(err => this._logger.warn('Lifecycle hook failed', { error: String(err) }));
|
|
354
|
+
}
|
|
355
|
+
return results;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Soft-delete multiple entities by IDs in a single batch operation.
|
|
359
|
+
*/
|
|
360
|
+
async bulkDelete(ids, orgId, userId) {
|
|
361
|
+
if (ids.length === 0)
|
|
362
|
+
return [];
|
|
363
|
+
const now = new Date();
|
|
364
|
+
const user = userId || 'system';
|
|
365
|
+
const conditions = [
|
|
366
|
+
(0, drizzle_orm_1.inArray)(this.schema.id, ids),
|
|
367
|
+
...this.buildConditions({}, orgId),
|
|
368
|
+
];
|
|
369
|
+
const deleted = await postgres_connection_1.db
|
|
370
|
+
.update(this.schema)
|
|
371
|
+
.set({
|
|
372
|
+
isActive: false,
|
|
373
|
+
updatedAt: now,
|
|
374
|
+
updatedBy: user,
|
|
375
|
+
deletedAt: now,
|
|
376
|
+
deletedBy: user,
|
|
377
|
+
})
|
|
378
|
+
.where((0, drizzle_orm_1.and)(...conditions))
|
|
379
|
+
.returning().then(r => drizzleRows(r));
|
|
380
|
+
for (const entity of deleted) {
|
|
381
|
+
this.onAfterDelete(entity.id, entity, userId).catch(err => this._logger.warn('Lifecycle hook failed', { error: String(err) }));
|
|
382
|
+
}
|
|
383
|
+
return deleted;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
exports.CrudService = CrudService;
|
|
387
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3J1ZC1zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FwaS9jcnVkLXNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7OztBQWtCdEMsa0NBRUM7QUFHRCxvQ0FFQztBQXZCRCx5REFBeUU7QUFDekUsNkNBQW9FO0FBR3BFLHlFQUFxRDtBQUVyRCxtRkFBbUY7QUFDbkYsTUFBTSxrQkFBa0IsR0FBRyxRQUFRLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxrQkFBa0IsSUFBSSxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFDakYsTUFBTSxjQUFjLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxJQUFJLE1BQU0sRUFBRSxFQUFFLENBQUMsQ0FBQztBQUUxRTs7Ozs7R0FLRztBQUNILFNBQWdCLFdBQVcsQ0FBSSxJQUFhO0lBQzFDLE9BQU8sSUFBVyxDQUFDO0FBQ3JCLENBQUM7QUFFRCx3RUFBd0U7QUFDeEUsU0FBZ0IsWUFBWSxDQUFDLElBQWE7SUFDeEMsT0FBTyxJQUEyQixDQUFDO0FBQ3JDLENBQUM7QUE4Q0Q7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F5Qkc7QUFDSCxNQUFzQixXQUFXO0lBd0JkLE9BQU8sR0FBRyxJQUFBLHVCQUFZLEVBQUMsYUFBYSxDQUFDLENBQUM7SUFFdkQsa0RBQWtEO0lBQzFDLFlBQVksQ0FBQyxFQUFVLEVBQUUsS0FBYztRQUM3QyxPQUFPLElBQUksQ0FBQyxlQUFlLENBQUMsRUFBRSxFQUFFLEVBQWlDLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFDNUUsQ0FBQztJQUVELGlFQUFpRTtJQUNqRSwyRUFBMkU7SUFFM0UsMkNBQTJDO0lBQ2pDLEtBQUssQ0FBQyxhQUFhLENBQUMsT0FBZ0IsRUFBRSxPQUFlLElBQWtCLENBQUM7SUFFbEYsd0NBQXdDO0lBQzlCLEtBQUssQ0FBQyxhQUFhLENBQUMsR0FB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QixFQUFFLEVBQUUsS0FBSyxFQUFFLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNqSSxDQUFDO1FBRUQsT0FBTyxPQUFPLENBQUM7SUFDakIsQ0FBQztDQUNGO0FBNWJELGtDQTRiQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgeyBOb3RGb3VuZEVycm9yLCBjcmVhdGVMb2dnZXIgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgeyBTUUwsIGVxLCBhbmQsIGFzYywgZGVzYywgc3FsLCBpbkFycmF5IH0gZnJvbSAnZHJpenpsZS1vcm0nO1xuaW1wb3J0IHR5cGUgeyBBbnlDb2x1bW4gfSBmcm9tICdkcml6emxlLW9ybS9jb2x1bW4nO1xuaW1wb3J0IHR5cGUgeyBQZ1RhYmxlIH0gZnJvbSAnZHJpenpsZS1vcm0vcGctY29yZSc7XG5pbXBvcnQgeyBkYiB9IGZyb20gJy4uL2RhdGFiYXNlL3Bvc3RncmVzLWNvbm5lY3Rpb24nO1xuXG4vKiogUGFnaW5hdGlvbiBkZWZhdWx0cyDigJQgcmVhZCBmcm9tIGVudiB0byBtYXRjaCBDb3JlQ29uc3RhbnRzIGluIHBpcGVsaW5lLWNvcmUuICovXG5jb25zdCBERUZBVUxUX1BBR0VfTElNSVQgPSBwYXJzZUludChwcm9jZXNzLmVudi5ERUZBVUxUX1BBR0VfTElNSVQgfHwgJzEwMCcsIDEwKTtcbmNvbnN0IE1BWF9QQUdFX0xJTUlUID0gcGFyc2VJbnQocHJvY2Vzcy5lbnYuTUFYX1BBR0VfTElNSVQgfHwgJzEwMDAnLCAxMCk7XG5cbi8qKlxuICogQ2FzdCBEcml6emxlIHF1ZXJ5IHJlc3VsdHMgdG8gYSB0eXBlZCBhcnJheS5cbiAqIERyaXp6bGUncyBnZW5lcmljIHJldHVybiB0eXBlIChgUGdTZWxlY3RCYXNlPC4uLj5gKSBkb2Vzbid0IG5hcnJvdyB0byBvdXJcbiAqIGVudGl0eSBnZW5lcmljcywgc28gYW4gZXhwbGljaXQgY2FzdCBpcyBuZWVkZWQuIENlbnRyYWxpc2VkIGhlcmUgc28gZXZlcnlcbiAqIGNhbGwtc2l0ZSBzdGF5cyBvbmUtbGluZXIgY2xlYW4gYW5kIHRoZSBjYXN0IGlzIGRvY3VtZW50ZWQgaW4gb25lIHBsYWNlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZHJpenpsZVJvd3M8VD4ocm93czogdW5rbm93bik6IFRbXSB7XG4gIHJldHVybiByb3dzIGFzIFRbXTtcbn1cblxuLyoqIENhc3QgYSBEcml6emxlIGFnZ3JlZ2F0ZSByZXN1bHQgdG8gZXh0cmFjdCBgW3sgY291bnQ6IG51bWJlciB9XWAuICovXG5leHBvcnQgZnVuY3Rpb24gZHJpenpsZUNvdW50KHJvd3M6IHVua25vd24pOiBbeyBjb3VudDogbnVtYmVyIH1dIHtcbiAgcmV0dXJuIHJvd3MgYXMgW3sgY291bnQ6IG51bWJlciB9XTtcbn1cblxuLyoqXG4gKiBCYXNlIGludGVyZmFjZSBmb3IgZW50aXRpZXMgd2l0aCBjb21tb24gZmllbGRzXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQmFzZUVudGl0eSB7XG4gIGlkOiBzdHJpbmc7XG4gIG9yZ0lkOiBzdHJpbmc7XG4gIGlzRGVmYXVsdDogYm9vbGVhbjtcbiAgY3JlYXRlZEF0OiBEYXRlO1xuICB1cGRhdGVkQXQ6IERhdGU7XG4gIGNyZWF0ZWRCeTogc3RyaW5nO1xuICB1cGRhdGVkQnk6IHN0cmluZztcbiAgW2tleTogc3RyaW5nXTogdW5rbm93bjtcbn1cblxuLyoqXG4gKiBQYWdpbmF0aW9uIGFuZCBzb3J0aW5nIG9wdGlvbnNcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBRdWVyeU9wdGlvbnMge1xuICBsaW1pdD86IG51bWJlcjtcbiAgb2Zmc2V0PzogbnVtYmVyO1xuICBzb3J0Qnk/OiBzdHJpbmc7XG4gIHNvcnRPcmRlcj86ICdhc2MnIHwgJ2Rlc2MnO1xuICAvKiogV2hlbiB0cnVlLCBydW5zIGEgc2VwYXJhdGUgQ09VTlQoKikgcXVlcnkgdG8gaW5jbHVkZSBleGFjdCB0b3RhbC4gRGVmYXVsdDogZmFsc2UuICovXG4gIGluY2x1ZGVUb3RhbD86IGJvb2xlYW47XG4gIC8qKiBDdXJzb3ItYmFzZWQgcGFnaW5hdGlvbjogZmV0Y2ggcm93cyBhZnRlciB0aGlzIGN1cnNvciB2YWx1ZSAodXNlcyBzb3J0QnkgY29sdW1uKS4gKi9cbiAgY3Vyc29yPzogc3RyaW5nO1xuICAvKiogU3BhcnNlIGZpZWxkc2V0OiBjb2x1bW4gbmFtZXMgdG8gc2VsZWN0LiBSZXR1cm5zIGFsbCBjb2x1bW5zIHdoZW4gb21pdHRlZC4gKi9cbiAgZmllbGRzPzogc3RyaW5nW107XG59XG5cbi8qKlxuICogUGFnaW5hdGVkIHJlc3VsdCB3aXRoIG1ldGFkYXRhXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUGFnaW5hdGVkUmVzdWx0PFQ+IHtcbiAgZGF0YTogVFtdO1xuICAvKiogVG90YWwgY291bnQgb2YgbWF0Y2hpbmcgZW50aXRpZXMuIE9ubHkgcHJlc2VudCB3aGVuIGluY2x1ZGVUb3RhbCBpcyB0cnVlLiAqL1xuICB0b3RhbD86IG51bWJlcjtcbiAgbGltaXQ6IG51bWJlcjtcbiAgb2Zmc2V0OiBudW1iZXI7XG4gIGhhc01vcmU6IGJvb2xlYW47XG4gIC8qKiBDdXJzb3IgcG9pbnRpbmcgdG8gdGhlIGxhc3QgaXRlbSwgZm9yIGN1cnNvci1iYXNlZCBwYWdpbmF0aW9uLiAqL1xuICBuZXh0Q3Vyc29yPzogc3RyaW5nO1xufVxuXG4vKipcbiAqIEFic3RyYWN0IENSVUQgc2VydmljZSB3aXRoIGFjY2VzcyBjb250cm9sIGFuZCBjb21tb24gb3BlcmF0aW9ucy5cbiAqXG4gKiBAdHlwZVBhcmFtIFRFbnRpdHkgLSBFbnRpdHkgdHlwZSBleHRlbmRpbmcgQmFzZUVudGl0eVxuICogQHR5cGVQYXJhbSBURmlsdGVyIC0gRmlsdGVyIHR5cGUgZm9yIHF1ZXJ5IHBhcmFtZXRlcnNcbiAqIEB0eXBlUGFyYW0gVEluc2VydCAtIEluc2VydCBEVE8gdHlwZVxuICogQHR5cGVQYXJhbSBUVXBkYXRlIC0gVXBkYXRlIERUTyB0eXBlXG4gKlxuICogVHlwZSBhc3NlcnRpb25zIChgYXMgYW55YCwgYGFzIHVua25vd24gYXMgVGApIGFyZSB1c2VkIHRocm91Z2hvdXQgZm9yIERyaXp6bGUgT1JNIGNvbXBhdGliaWxpdHkuXG4gKiBUaGlzIGlzIHNhZmUgYmVjYXVzZTogYWNjZXNzIGNvbnRyb2wgZmlsdGVycyBieSBvcmdJZCwgc2NoZW1hIHZhbGlkYXRpb24gaXMgYXQgdGhlIERCIGxldmVsLFxuICogYW5kIGVhY2ggc3ViY2xhc3MgaXMgdGVzdGVkIGZvciB0eXBlIGNvcnJlY3RuZXNzLlxuICpcbiAqIEVycm9ycyBhcmUgbm90IGNhdWdodCBoZXJlIOKAlCB0aGV5IHByb3BhZ2F0ZSB0byB0aGUgcm91dGUtbGV2ZWwgZXJyb3IgaGFuZGxlciAoYHdpdGhSb3V0ZWApXG4gKiB3aGljaCBwcm92aWRlcyBjb25zaXN0ZW50IGxvZ2dpbmcgd2l0aCByZXF1ZXN0IGNvbnRleHQuXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNsYXNzIFBpcGVsaW5lU2VydmljZSBleHRlbmRzIENydWRTZXJ2aWNlPFBpcGVsaW5lLCBQaXBlbGluZUZpbHRlciwgUGlwZWxpbmVJbnNlcnQsIFBpcGVsaW5lVXBkYXRlPiB7XG4gKiAgIHByb3RlY3RlZCBnZXQgc2NoZW1hKCkgeyByZXR1cm4gc2NoZW1hLnBpcGVsaW5lOyB9XG4gKiAgIHByb3RlY3RlZCBidWlsZENvbmRpdGlvbnMoZmlsdGVyLCBvcmdJZCkgeyByZXR1cm4gYnVpbGRQaXBlbGluZUNvbmRpdGlvbnMoZmlsdGVyLCBvcmdJZCk7IH1cbiAqICAgcHJvdGVjdGVkIGdldFNvcnRDb2x1bW4oc29ydEJ5KSB7IHJldHVybiBzb3J0Q29sdW1uTWFwW3NvcnRCeV0gPz8gbnVsbDsgfVxuICogICBwcm90ZWN0ZWQgZ2V0UHJvamVjdENvbHVtbigpIHsgcmV0dXJuIHNjaGVtYS5waXBlbGluZS5wcm9qZWN0OyB9XG4gKiAgIHByb3RlY3RlZCBnZXRPcmdDb2x1bW4oKSB7IHJldHVybiBzY2hlbWEucGlwZWxpbmUub3JnYW5pemF0aW9uOyB9XG4gKiB9XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGFic3RyYWN0IGNsYXNzIENydWRTZXJ2aWNlPFxuICBURW50aXR5IGV4dGVuZHMgQmFzZUVudGl0eSxcbiAgVEZpbHRlcixcbiAgVEluc2VydCxcbiAgVFVwZGF0ZSxcbj4ge1xuICAvKiogRHJpenpsZSBzY2hlbWEgdGFibGUgZm9yIHRoaXMgZW50aXR5ICovXG4gIHByb3RlY3RlZCBhYnN0cmFjdCBnZXQgc2NoZW1hKCk6IFBnVGFibGU7XG5cbiAgLyoqIEJ1aWxkIFNRTCBjb25kaXRpb25zIGZvciBmaWx0ZXJpbmcgZW50aXRpZXMgKi9cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGJ1aWxkQ29uZGl0aW9ucyhmaWx0ZXI6IFBhcnRpYWw8VEZpbHRlcj4sIG9yZ0lkPzogc3RyaW5nKTogU1FMW107XG5cbiAgLyoqIEdldCB0aGUgc2NoZW1hIGNvbHVtbiBmb3Igc29ydGluZyBieSBmaWVsZCBuYW1lICovXG4gIHByb3RlY3RlZCBhYnN0cmFjdCBnZXRTb3J0Q29sdW1uKHNvcnRCeTogc3RyaW5nKTogQW55Q29sdW1uIHwgbnVsbDtcblxuICAvKiogR2V0IHRoZSBwcm9qZWN0IGNvbHVtbiBmb3Igc2V0RGVmYXVsdCBzY29waW5nIChudWxsIGlmIGVudGl0eSBoYXMgbm8gcHJvamVjdCBzY29wZSkgKi9cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGdldFByb2plY3RDb2x1bW4oKTogQW55Q29sdW1uIHwgbnVsbDtcblxuICAvKiogR2V0IHRoZSBvcmdhbml6YXRpb24gY29sdW1uIGZvciBzZXREZWZhdWx0IHNjb3BpbmcgKi9cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGdldE9yZ0NvbHVtbigpOiBBbnlDb2x1bW47XG5cbiAgLyoqIEdldCB0aGUgdW5pcXVlIGNvbnN0cmFpbnQgY29sdW1ucyBmb3Igb25Db25mbGljdERvVXBkYXRlICovXG4gIHByb3RlY3RlZCBhYnN0cmFjdCBnZXQgY29uZmxpY3RUYXJnZXQoKTogQW55Q29sdW1uW107XG5cbiAgcHJpdmF0ZSByZWFkb25seSBfbG9nZ2VyID0gY3JlYXRlTG9nZ2VyKCdDcnVkU2VydmljZScpO1xuXG4gIC8qKiBCdWlsZCBjb25kaXRpb25zIGZvciBhIHNpbmdsZSBlbnRpdHkgYnkgSUQuICovXG4gIHByaXZhdGUgaWRDb25kaXRpb25zKGlkOiBzdHJpbmcsIG9yZ0lkPzogc3RyaW5nKTogU1FMW10ge1xuICAgIHJldHVybiB0aGlzLmJ1aWxkQ29uZGl0aW9ucyh7IGlkIH0gYXMgdW5rbm93biBhcyBQYXJ0aWFsPFRGaWx0ZXI+LCBvcmdJZCk7XG4gIH1cblxuICAvLyBMaWZlY3ljbGUgaG9va3Mg4oCUIG92ZXJyaWRlIGluIHN1YmNsYXNzZXMgdG8gcmVhY3QgdG8gbXV0YXRpb25zXG4gIC8vIFRoZXNlIGFyZSBmaXJlLWFuZC1mb3JnZXQ6IGVycm9ycyBhcmUgbG9nZ2VkIGJ1dCBuZXZlciBibG9jayB0aGUgY2FsbGVyLlxuXG4gIC8qKiBDYWxsZWQgYWZ0ZXIgYSBuZXcgZW50aXR5IGlzIGNyZWF0ZWQgKi9cbiAgcHJvdGVjdGVkIGFzeW5jIG9uQWZ0ZXJDcmVhdGUoX2VudGl0eTogVEVudGl0eSwgX3VzZXJJZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7fVxuXG4gIC8qKiBDYWxsZWQgYWZ0ZXIgYW4gZW50aXR5IGlzIHVwZGF0ZWQgKi9cbiAgcHJvdGVjdGVkIGFzeW5jIG9uQWZ0ZXJVcGRhdGUoX2lkOiBzdHJpbmcsIF9lbnRpdHk6IFRFbnRpdHksIF91c2VySWQ6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge31cblxuICAvKiogQ2FsbGVkIGFmdGVyIGFuIGVudGl0eSBpcyBzb2Z0LWRlbGV0ZWQgKi9cbiAgcHJvdGVjdGVkIGFzeW5jIG9uQWZ0ZXJEZWxldGUoX2lkOiBzdHJpbmcsIF9lbnRpdHk6IFRFbnRpdHksIF91c2VySWQ6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge31cblxuICAvKipcbiAgICogRmluZCBlbnRpdGllcyBtYXRjaGluZyBmaWx0ZXIgY3JpdGVyaWFcbiAgICpcbiAgICogQHBhcmFtIGZpbHRlciAtIEZpbHRlciBjcml0ZXJpYVxuICAgKiBAcGFyYW0gb3JnSWQgLSBVc2VyJ3Mgb3JnYW5pemF0aW9uIElEIChvcHRpb25hbCDigJQgb21pdCBmb3IgYW5vbnltb3VzL3N5c3RlbS1wdWJsaWMtb25seSBhY2Nlc3MpXG4gICAqL1xuICBhc3luYyBmaW5kKGZpbHRlcjogUGFydGlhbDxURmlsdGVyPiwgb3JnSWQ/OiBzdHJpbmcpOiBQcm9taXNlPFRFbnRpdHlbXT4ge1xuICAgIGNvbnN0IGNvbmRpdGlvbnMgPSB0aGlzLmJ1aWxkQ29uZGl0aW9ucyhmaWx0ZXIsIG9yZ0lkKTtcblxuICAgIHJldHVybiBkYlxuICAgICAgLnNlbGVjdCgpXG4gICAgICAuZnJvbSh0aGlzLnNjaGVtYSlcbiAgICAgIC53aGVyZShhbmQoLi4uY29uZGl0aW9ucykpLnRoZW4ociA9PiBkcml6emxlUm93czxURW50aXR5PihyKSk7XG4gIH1cblxuICAvKipcbiAgICogRmluZCBlbnRpdGllcyB3aXRoIHBhZ2luYXRpb24gYW5kIHNvcnRpbmdcbiAgICpcbiAgICogQHBhcmFtIGZpbHRlciAtIEZpbHRlciBjcml0ZXJpYVxuICAgKiBAcGFyYW0gb3JnSWQgLSBVc2VyJ3Mgb3JnYW5pemF0aW9uIElEIChvcHRpb25hbCDigJQgb21pdCBmb3IgYW5vbnltb3VzL3N5c3RlbS1wdWJsaWMtb25seSBhY2Nlc3MpXG4gICAqIEBwYXJhbSBvcHRpb25zIC0gUGFnaW5hdGlvbiBhbmQgc29ydGluZyBvcHRpb25zXG4gICAqL1xuICBhc3luYyBmaW5kUGFnaW5hdGVkKFxuICAgIGZpbHRlcjogUGFydGlhbDxURmlsdGVyPixcbiAgICBvcmdJZD86IHN0cmluZyxcbiAgICBvcHRpb25zOiBRdWVyeU9wdGlvbnMgPSB7fSxcbiAgKTogUHJvbWlzZTxQYWdpbmF0ZWRSZXN1bHQ8VEVudGl0eT4+IHtcbiAgICBjb25zdCB7IGxpbWl0OiByYXdMaW1pdCA9IERFRkFVTFRfUEFHRV9MSU1JVCwgb2Zmc2V0ID0gMCwgc29ydEJ5LCBzb3J0T3JkZXIgPSAnYXNjJywgaW5jbHVkZVRvdGFsID0gZmFsc2UsIGN1cnNvciwgZmllbGRzIH0gPSBvcHRpb25zO1xuICAgIGNvbnN0IGxpbWl0ID0gTWF0aC5taW4oTWF0aC5tYXgoMSwgcmF3TGltaXQpLCBNQVhfUEFHRV9MSU1JVCk7XG5cbiAgICAvLyBDdXJzb3IgYW5kIG9mZnNldCBhcmUgbXV0dWFsbHkgZXhjbHVzaXZlIOKAlCBjdXJzb3IgdGFrZXMgcHJlY2VkZW5jZVxuICAgIGNvbnN0IHVzZUN1cnNvciA9ICEhKGN1cnNvciAmJiBzb3J0QnkpO1xuXG4gICAgY29uc3QgY29uZGl0aW9ucyA9IHRoaXMuYnVpbGRDb25kaXRpb25zKGZpbHRlciwgb3JnSWQpO1xuXG4gICAgLy8gQ3Vyc29yLWJhc2VkIHBhZ2luYXRpb246IGFkZCBXSEVSRSBjbGF1c2UgZm9yIGtleXNldCBwYWdpbmF0aW9uXG4gICAgaWYgKHVzZUN1cnNvcikge1xuICAgICAgY29uc3Qgc29ydENvbHVtbiA9IHRoaXMuZ2V0U29ydENvbHVtbihzb3J0QnkpO1xuICAgICAgaWYgKHNvcnRDb2x1bW4pIHtcbiAgICAgICAgY29uc3Qgb3AgPSBzb3J0T3JkZXIgPT09ICdkZXNjJ1xuICAgICAgICAgID8gc3FsYCR7c29ydENvbHVtbn0gPCAke2N1cnNvcn1gXG4gICAgICAgICAgOiBzcWxgJHtzb3J0Q29sdW1ufSA+ICR7Y3Vyc29yfWA7XG4gICAgICAgIGNvbmRpdGlvbnMucHVzaChvcCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gQnVpbGQgU0VMRUNUIOKAlCBzcGFyc2UgZmllbGRzZXQgd2hlbiBmaWVsZHMgYXJlIHNwZWNpZmllZFxuICAgIGNvbnN0IHNlbGVjdFNwZWMgPSBmaWVsZHMgPyB0aGlzLmJ1aWxkRmllbGRTZWxlY3QoZmllbGRzKSA6IHVuZGVmaW5lZDtcbiAgICBsZXQgcXVlcnkgPSBzZWxlY3RTcGVjXG4gICAgICA/IGRiLnNlbGVjdChzZWxlY3RTcGVjIGFzIGFueSkuZnJvbSh0aGlzLnNjaGVtYSkud2hlcmUoYW5kKC4uLmNvbmRpdGlvbnMpKVxuICAgICAgOiBkYi5zZWxlY3QoKS5mcm9tKHRoaXMuc2NoZW1hKS53aGVyZShhbmQoLi4uY29uZGl0aW9ucykpO1xuXG4gICAgaWYgKHNvcnRCeSkge1xuICAgICAgY29uc3Qgc29ydENvbHVtbiA9IHRoaXMuZ2V0U29ydENvbHVtbihzb3J0QnkpO1xuICAgICAgaWYgKHNvcnRDb2x1bW4pIHtcbiAgICAgICAgcXVlcnkgPSBxdWVyeS5vcmRlckJ5KHNvcnRPcmRlciA9PT0gJ2Rlc2MnID8gZGVzYyhzb3J0Q29sdW1uKSA6IGFzYyhzb3J0Q29sdW1uKSkgYXMgYW55O1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEZldGNoIGxpbWl0KzEgdG8gZGV0ZWN0IGhhc01vcmUgd2l0aG91dCBDT1VOVCgqKVxuICAgIGNvbnN0IGVmZmVjdGl2ZU9mZnNldCA9IHVzZUN1cnNvciA/IDAgOiBvZmZzZXQ7XG4gICAgY29uc3Qgcm93cyA9IGF3YWl0IHF1ZXJ5XG4gICAgICAubGltaXQobGltaXQgKyAxKVxuICAgICAgLm9mZnNldChlZmZlY3RpdmVPZmZzZXQpLnRoZW4ociA9PiBkcml6emxlUm93czxURW50aXR5PihyKSk7XG5cbiAgICBjb25zdCBoYXNNb3JlID0gcm93cy5sZW5ndGggPiBsaW1pdDtcbiAgICBjb25zdCBkYXRhID0gaGFzTW9yZSA/IHJvd3Muc2xpY2UoMCwgbGltaXQpIDogcm93cztcblxuICAgIGNvbnN0IHJlc3VsdDogUGFnaW5hdGVkUmVzdWx0PFRFbnRpdHk+ID0geyBkYXRhLCBsaW1pdCwgb2Zmc2V0OiBlZmZlY3RpdmVPZmZzZXQsIGhhc01vcmUgfTtcblxuICAgIC8vIFByb3ZpZGUgbmV4dCBjdXJzb3IgZnJvbSBsYXN0IGl0ZW0ncyBzb3J0IGNvbHVtbiB2YWx1ZVxuICAgIGlmIChkYXRhLmxlbmd0aCA+IDAgJiYgc29ydEJ5KSB7XG4gICAgICBjb25zdCBsYXN0SXRlbSA9IGRhdGFbZGF0YS5sZW5ndGggLSAxXSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICAgIGNvbnN0IGN1cnNvclZhbHVlID0gbGFzdEl0ZW1bc29ydEJ5XTtcbiAgICAgIGlmIChjdXJzb3JWYWx1ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJlc3VsdC5uZXh0Q3Vyc29yID0gY3Vyc29yVmFsdWUgaW5zdGFuY2VvZiBEYXRlID8gY3Vyc29yVmFsdWUudG9JU09TdHJpbmcoKSA6IFN0cmluZyhjdXJzb3JWYWx1ZSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gT25seSBydW4gdGhlIENPVU5UKCopIHF1ZXJ5IHdoZW4gdGhlIGNhbGxlciBleHBsaWNpdGx5IG5lZWRzIHRoZSB0b3RhbFxuICAgIGlmIChpbmNsdWRlVG90YWwpIHtcbiAgICAgIGNvbnN0IGJhc2VDb25kaXRpb25zID0gdGhpcy5idWlsZENvbmRpdGlvbnMoZmlsdGVyLCBvcmdJZCk7XG4gICAgICBjb25zdCBbY291bnRSZXN1bHRdID0gYXdhaXQgZGJcbiAgICAgICAgLnNlbGVjdCh7IGNvdW50OiBzcWw8bnVtYmVyPmBjb3VudCgqKTo6aW50YCB9KVxuICAgICAgICAuZnJvbSh0aGlzLnNjaGVtYSlcbiAgICAgICAgLndoZXJlKGFuZCguLi5iYXNlQ29uZGl0aW9ucykpLnRoZW4ociA9PiBkcml6emxlQ291bnQocikpO1xuICAgICAgcmVzdWx0LnRvdGFsID0gY291bnRSZXN1bHQ/LmNvdW50IHx8IDA7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZCBhIGNvbHVtbiBzZWxlY3Rpb24gbWFwIGZvciBzcGFyc2UgZmllbGRzZXRzLlxuICAgKiBGYWxscyBiYWNrIHRvIGZ1bGwgc2VsZWN0IGlmIG5vIG1hdGNoaW5nIGNvbHVtbnMgZm91bmQuXG4gICAqL1xuICBwcml2YXRlIGJ1aWxkRmllbGRTZWxlY3QoZmllbGRzOiBzdHJpbmdbXSk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgdW5kZWZpbmVkIHtcbiAgICBpZiAoZmllbGRzLmxlbmd0aCA9PT0gMCkgcmV0dXJuIHVuZGVmaW5lZDtcblxuICAgIGNvbnN0IGNvbHVtbnM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge307XG4gICAgLy8gQWx3YXlzIGluY2x1ZGUgaWQgZm9yIGVudGl0eSBpZGVudGl0eVxuICAgIGNvbHVtbnMuaWQgPSAodGhpcy5zY2hlbWEgYXMgYW55KS5pZDtcblxuICAgIGZvciAoY29uc3QgZmllbGQgb2YgZmllbGRzKSB7XG4gICAgICBpZiAoZmllbGQgPT09ICdpZCcpIGNvbnRpbnVlOyAvLyBBbHJlYWR5IGluY2x1ZGVkXG4gICAgICBjb25zdCBjb2wgPSAodGhpcy5zY2hlbWEgYXMgYW55KVtmaWVsZF07XG4gICAgICBpZiAoY29sKSBjb2x1bW5zW2ZpZWxkXSA9IGNvbDtcbiAgICB9XG5cbiAgICAvLyBBdCBtaW5pbXVtIHdlJ2xsIGhhdmUgeyBpZCB9LCB3aGljaCBpcyB2YWxpZFxuICAgIHJldHVybiBjb2x1bW5zO1xuICB9XG5cbiAgLyoqXG4gICAqIENvdW50IGVudGl0aWVzIG1hdGNoaW5nIGZpbHRlciBjcml0ZXJpYVxuICAgKlxuICAgKiBAcGFyYW0gZmlsdGVyIC0gRmlsdGVyIGNyaXRlcmlhXG4gICAqIEBwYXJhbSBvcmdJZCAtIFVzZXIncyBvcmdhbml6YXRpb24gSUQgKG9wdGlvbmFsIOKAlCBvbWl0IGZvciBhbm9ueW1vdXMvc3lzdGVtLXB1YmxpYy1vbmx5IGFjY2VzcylcbiAgICovXG4gIGFzeW5jIGNvdW50KGZpbHRlcjogUGFydGlhbDxURmlsdGVyPiwgb3JnSWQ/OiBzdHJpbmcpOiBQcm9taXNlPG51bWJlcj4ge1xuICAgIGNvbnN0IGNvbmRpdGlvbnMgPSB0aGlzLmJ1aWxkQ29uZGl0aW9ucyhmaWx0ZXIsIG9yZ0lkKTtcblxuICAgIGNvbnN0IFtyZXN1bHRdID0gYXdhaXQgZGJcbiAgICAgIC5zZWxlY3QoeyBjb3VudDogc3FsPG51bWJlcj5gY291bnQoKik6OmludGAgfSlcbiAgICAgIC5mcm9tKHRoaXMuc2NoZW1hKVxuICAgICAgLndoZXJlKGFuZCguLi5jb25kaXRpb25zKSkudGhlbihyID0+IGRyaXp6bGVDb3VudChyKSk7XG5cbiAgICByZXR1cm4gcmVzdWx0Py5jb3VudCB8fCAwO1xuICB9XG5cbiAgLyoqXG4gICAqIEZpbmQgYSBzaW5nbGUgZW50aXR5IGJ5IElEXG4gICAqXG4gICAqIEBwYXJhbSBpZCAtIEVudGl0eSBJRFxuICAgKiBAcGFyYW0gb3JnSWQgLSBVc2VyJ3Mgb3JnYW5pemF0aW9uIElEIChvcHRpb25hbCDigJQgb21pdCBmb3IgYW5vbnltb3VzL3N5c3RlbS1wdWJsaWMtb25seSBhY2Nlc3MpXG4gICAqL1xuICBhc3luYyBmaW5kQnlJZChpZDogc3RyaW5nLCBvcmdJZD86IHN0cmluZyk6IFByb21pc2U8VEVudGl0eSB8IG51bGw+IHtcbiAgICBjb25zdCBjb25kaXRpb25zID0gdGhpcy5pZENvbmRpdGlvbnMoaWQsIG9yZ0lkKTtcblxuICAgIGNvbnN0IHJlc3VsdHMgPSBhd2FpdCBkYlxuICAgICAgLnNlbGVjdCgpXG4gICAgICAuZnJvbSh0aGlzLnNjaGVtYSlcbiAgICAgIC53aGVyZShhbmQoLi4uY29uZGl0aW9ucykpXG4gICAgICAubGltaXQoMSkudGhlbihyID0+IGRyaXp6bGVSb3dzPFRFbnRpdHk+KHIpKTtcblxuICAgIHJldHVybiByZXN1bHRzWzBdIHx8IG51bGw7XG4gIH1cblxuICAvLyBNdXRhdGlvbiBvcGVyYXRpb25zXG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIG5ldyBlbnRpdHlcbiAgICovXG4gIGFzeW5jIGNyZWF0ZShkYXRhOiBUSW5zZXJ0LCB1c2VySWQ6IHN0cmluZyk6IFByb21pc2U8VEVudGl0eT4ge1xuICAgIGNvbnN0IFtjcmVhdGVkXSA9IGF3YWl0IGRiXG4gICAgICAuaW5zZXJ0KHRoaXMuc2NoZW1hKVxuICAgICAgLnZhbHVlcyh7XG4gICAgICAgIC4uLmRhdGEsXG4gICAgICAgIGNyZWF0ZWRCeTogdXNlcklkIHx8ICdzeXN0ZW0nLFxuICAgICAgICB1cGRhdGVkQnk6IHVzZXJJZCB8fCAnc3lzdGVtJyxcbiAgICAgIH0gYXMgYW55KVxuICAgICAgLm9uQ29uZmxpY3REb1VwZGF0ZSh7XG4gICAgICAgIHRhcmdldDogdGhpcy5jb25mbGljdFRhcmdldCBhcyBhbnksXG4gICAgICAgIHNldDoge1xuICAgICAgICAgIC4uLmRhdGEsXG4gICAgICAgICAgdXBkYXRlZEF0OiBuZXcgRGF0ZSgpLFxuICAgICAgICAgIHVwZGF0ZWRCeTogdXNlcklkIHx8ICdzeXN0ZW0nLFxuICAgICAgICB9IGFzIGFueSxcbiAgICAgIH0pXG4gICAgICAucmV0dXJuaW5nKCkudGhlbihyID0+IGRyaXp6bGVSb3dzPFRFbnRpdHk+KHIpKTtcblxuICAgIHRoaXMub25BZnRlckNyZWF0ZShjcmVhdGVkLCB1c2VySWQpLmNhdGNoKGVyciA9PiB0aGlzLl9sb2dnZXIud2FybignTGlmZWN5Y2xlIGhvb2sgZmFpbGVkJywgeyBlcnJvcjogU3RyaW5nKGVycikgfSkpO1xuXG4gICAgcmV0dXJuIGNyZWF0ZWQ7XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlIGFuIGV4aXN0aW5nIGVudGl0eVxuICAgKi9cbiAgYXN5bmMgdXBkYXRlKFxuICAgIGlkOiBzdHJpbmcsXG4gICAgZGF0YTogUGFydGlhbDxUVXBkYXRlPixcbiAgICBvcmdJZDogc3RyaW5nLFxuICAgIHVzZXJJZDogc3RyaW5nLFxuICApOiBQcm9taXNlPFRFbnRpdHkgfCBudWxsPiB7XG4gICAgY29uc3QgY29uZGl0aW9ucyA9IHRoaXMuaWRDb25kaXRpb25zKGlkLCBvcmdJZCk7XG5cbiAgICBjb25zdCBbdXBkYXRlZF0gPSBhd2FpdCBkYlxuICAgICAgLnVwZGF0ZSh0aGlzLnNjaGVtYSlcbiAgICAgIC5zZXQoe1xuICAgICAgICAuLi5kYXRhLFxuICAgICAgICB1cGRhdGVkQXQ6IG5ldyBEYXRlKCksXG4gICAgICAgIHVwZGF0ZWRCeTogdXNlcklkIHx8ICdzeXN0ZW0nLFxuICAgICAgfSBhcyBhbnkpXG4gICAgICAud2hlcmUoYW5kKC4uLmNvbmRpdGlvbnMpKVxuICAgICAgLnJldHVybmluZygpLnRoZW4ociA9PiBkcml6emxlUm93czxURW50aXR5PihyKSk7XG5cbiAgICBpZiAodXBkYXRlZCkge1xuICAgICAgdGhpcy5vbkFmdGVyVXBkYXRlKGlkLCB1cGRhdGVkLCB1c2VySWQpLmNhdGNoKGVyciA9PiB0aGlzLl9sb2dnZXIud2FybignTGlmZWN5Y2xlIGhvb2sgZmFpbGVkJywgeyBlcnJvcjogU3RyaW5nKGVycikgfSkpO1xuICAgIH1cblxuICAgIHJldHVybiB1cGRhdGVkIHx8IG51bGw7XG4gIH1cblxuICAvKipcbiAgICogRGVsZXRlIGFuIGVudGl0eSAoc29mdCBkZWxldGUgYnkgc2V0dGluZyBpc0FjdGl2ZSA9IGZhbHNlKVxuICAgKi9cbiAgYXN5bmMgZGVsZXRlKGlkOiBzdHJpbmcsIG9yZ0lkOiBzdHJpbmcsIHVzZXJJZDogc3RyaW5nKTogUHJvbWlzZTxURW50aXR5IHwgbnVsbD4ge1xuICAgIGNvbnN0IGNvbmRpdGlvbnMgPSB0aGlzLmlkQ29uZGl0aW9ucyhpZCwgb3JnSWQpO1xuXG4gICAgY29uc3QgW2RlbGV0ZWRdID0gYXdhaXQgZGJcbiAgICAgIC51cGRhdGUodGhpcy5zY2hlbWEpXG4gICAgICAuc2V0KHtcbiAgICAgICAgaXNBY3RpdmU6IGZhbHNlLFxuICAgICAgICB1cGRhdGVkQXQ6IG5ldyBEYXRlKCksXG4gICAgICAgIHVwZGF0ZWRCeTogdXNlcklkIHx8ICdzeXN0ZW0nLFxuICAgICAgICBkZWxldGVkQXQ6IG5ldyBEYXRlKCksXG4gICAgICAgIGRlbGV0ZWRCeTogdXNlcklkIHx8ICdzeXN0ZW0nLFxuICAgICAgfSBhcyBhbnkpXG4gICAgICAud2hlcmUoYW5kKC4uLmNvbmRpdGlvbnMpKVxuICAgICAgLnJldHVybmluZygpLnRoZW4ociA9PiBkcml6emxlUm93czxURW50aXR5PihyKSk7XG5cbiAgICBpZiAoZGVsZXRlZCkge1xuICAgICAgdGhpcy5vbkFmdGVyRGVsZXRlKGlkLCBkZWxldGVkLCB1c2VySWQpLmNhdGNoKGVyciA9PiB0aGlzLl9sb2dnZXIud2FybignTGlmZWN5Y2xlIGhvb2sgZmFpbGVkJywgeyBlcnJvcjogU3RyaW5nKGVycikgfSkpO1xuICAgIH1cblxuICAgIHJldHVybiBkZWxldGVkIHx8IG51bGw7XG4gIH1cblxuICAvKipcbiAgICogU2V0IGFuIGVudGl0eSBhcyB0aGUgZGVmYXVsdCBmb3IgYSBwcm9qZWN0L29yZ2FuaXphdGlvbiBzY29wZS5cbiAgICogTWFya3MgYWxsIG90aGVyIGVudGl0aWVzIGFzIG5vbi1kZWZhdWx0LCB0aGVuIHNldHMgdGhlIHNwZWNpZmllZCBlbnRpdHkuXG4gICAqIFVzZXMgYSB0cmFuc2FjdGlvbiB0byBlbnN1cmUgYXRvbWljaXR5LlxuICAgKi9cbiAgYXN5bmMgc2V0RGVmYXVsdChcbiAgICBwcm9qZWN0OiBzdHJpbmcsXG4gICAgb3JnOiBzdHJpbmcsXG4gICAgaWQ6IHN0cmluZyxcbiAgICB1c2VySWQ6IHN0cmluZyxcbiAgKTogUHJvbWlzZTxURW50aXR5PiB7XG4gICAgcmV0dXJuIGRiLnRyYW5zYWN0aW9uKGFzeW5jICh0eCkgPT4ge1xuICAgICAgY29uc3Qgb3JnQ29sdW1uID0gdGhpcy5nZXRPcmdDb2x1bW4oKTtcbiAgICAgIGNvbnN0IHByb2plY3RDb2x1bW4gPSB0aGlzLmdldFByb2plY3RDb2x1bW4oKTtcblxuICAgICAgLy8gQnVpbGQgc2NvcGluZyBjb25kaXRpb25zIGZvciBjbGVhcmluZyBkZWZhdWx0c1xuICAgICAgY29uc3Qgc2NvcGVDb25kaXRpb25zID0gW1xuICAgICAgICBlcShvcmdDb2x1bW4sIG9yZyksXG4gICAgICAgIGVxKCh0aGlzLnNjaGVtYSBhcyBhbnkpLmlzRGVmYXVsdCwgdHJ1ZSksXG4gICAgICBdO1xuICAgICAgaWYgKHByb2plY3RDb2x1bW4pIHtcbiAgICAgICAgc2NvcGVDb25kaXRpb25zLnB1c2goZXEocHJvamVjdENvbHVtbiwgcHJvamVjdCkpO1xuICAgICAgfVxuXG4gICAgICAvLyBMb2NrIGV4aXN0aW5nIGRlZmF1bHRzIHdpdGggRk9SIFVQREFURSB0byBwcmV2ZW50IGNvbmN1cnJlbnQgc2V0RGVmYXVsdCByYWNlc1xuICAgICAgYXdhaXQgdHguZXhlY3V0ZShcbiAgICAgICAgc3FsYFNFTEVDVCBpZCBGUk9NICR7dGhpcy5zY2hlbWF9XG4gICAgICAgICAgICBXSEVSRSAke29yZ0NvbHVtbn0gPSAke29yZ31cbiAgICAgICAgICAgICAgQU5EICR7KHRoaXMuc2NoZW1hIGFzIGFueSkuaXNEZWZhdWx0fSA9IHRydWVcbiAgICAgICAgICAgICR7cHJvamVjdENvbHVtbiA/IHNxbGBBTkQgJHtwcm9qZWN0Q29sdW1ufSA9ICR7cHJvamVjdH1gIDogc3FsYGB9XG4gICAgICAgICAgICBGT1IgVVBEQVRFYCxcbiAgICAgICk7XG5cbiAgICAgIC8vIE1hcmsgYWxsIGVudGl0aWVzIGluIHNjb3BlIGFzIG5vbi1kZWZhdWx0XG4gICAgICBhd2FpdCB0eFxuICAgICAgICAudXBkYXRlKHRoaXMuc2NoZW1hKVxuICAgICAgICAuc2V0KHtcbiAgICAgICAgICBpc0RlZmF1bHQ6IGZhbHNlLFxuICAgICAgICAgIHVwZGF0ZWRBdDogbmV3IERhdGUoKSxcbiAgICAgICAgICB1cGRhdGVkQnk6IHVzZXJJZCB8fCAnc3lzdGVtJyxcbiAgICAgICAgfSBhcyBhbnkpXG4gICAgICAgIC53aGVyZShhbmQoLi4uc2NvcGVDb25kaXRpb25zKSk7XG5cbiAgICAgIC8vIFNldCB0aGUgc3BlY2lmaWVkIGVudGl0eSBhcyBkZWZhdWx0XG4gICAgICBjb25zdCBbdXBkYXRlZF0gPSBhd2FpdCB0eFxuICAgICAgICAudXBkYXRlKHRoaXMuc2NoZW1hKVxuICAgICAgICAuc2V0KHtcbiAgICAgICAgICBpc0RlZmF1bHQ6IHRydWUsXG4gICAgICAgICAgdXBkYXRlZEF0OiBuZXcgRGF0ZSgpLFxuICAgICAgICAgIHVwZGF0ZWRCeTogdXNlcklkIHx8ICdzeXN0ZW0nLFxuICAgICAgICB9IGFzIGFueSlcbiAgICAgICAgLndoZXJlKFxuICAgICAgICAgIGFuZChcbiAgICAgICAgICAgIGVxKCh0aGlzLnNjaGVtYSBhcyBhbnkpLmlkLCBpZCksXG4gICAgICAgICAgICBlcShvcmdDb2x1bW4sIG9yZyksXG4gICAgICAgICAgKSxcbiAgICAgICAgKVxuICAgICAgICAucmV0dXJuaW5nKCkudGhlbihyID0+IGRyaXp6bGVSb3dzPFRFbnRpdHk+KHIpKTtcblxuICAgICAgaWYgKCF1cGRhdGVkKSB7XG4gICAgICAgIHRocm93IG5ldyBOb3RGb3VuZEVycm9yKGBFbnRpdHkgd2l0aCBpZCAke2lkfSBub3QgZm91bmRgKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHVwZGF0ZWQ7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlIG11bHRpcGxlIGVudGl0aWVzIG1hdGNoaW5nIGZpbHRlclxuICAgKi9cbiAgYXN5bmMgdXBkYXRlTWFueShcbiAgICBmaWx0ZXI6IFBhcnRpYWw8VEZpbHRlcj4sXG4gICAgZGF0YTogUGFydGlhbDxUVXBkYXRlPixcbiAgICBvcmdJZDogc3RyaW5nLFxuICAgIHVzZXJJZDogc3RyaW5nLFxuICApOiBQcm9taXNlPFRFbnRpdHlbXT4ge1xuICAgIGNvbnN0IGNvbmRpdGlvbnMgPSB0aGlzLmJ1aWxkQ29uZGl0aW9ucyhmaWx0ZXIsIG9yZ0lkKTtcblxuICAgIHJldHVybiBkYlxuICAgICAgLnVwZGF0ZSh0aGlzLnNjaGVtYSlcbiAgICAgIC5zZXQoe1xuICAgICAgICAuLi5kYXRhLFxuICAgICAgICB1cGRhdGVkQXQ6IG5ldyBEYXRlKCksXG4gICAgICAgIHVwZGF0ZWRCeTogdXNlcklkIHx8ICdzeXN0ZW0nLFxuICAgICAgfSBhcyBhbnkpXG4gICAgICAud2hlcmUoYW5kKC4uLmNvbmRpdGlvbnMpKVxuICAgICAgLnJldHVybmluZygpLnRoZW4ociA9PiBkcml6emxlUm93czxURW50aXR5PihyKSk7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlIG11bHRpcGxlIGVudGl0aWVzIGluIGEgc2luZ2xlIGJhdGNoIGluc2VydC5cbiAgICogVXNlcyB1cHNlcnQgKG9uQ29uZmxpY3REb1VwZGF0ZSkg4oCUIGFsbCByb3dzIGFyZSBpbnNlcnRlZCBpbiBvbmUgcXVlcnkgcGVyIGNodW5rLlxuICAgKiBDaHVua3Mgb2YgMTAwIHRvIHN0YXkgd2l0aGluIFBvc3RncmVTUUwgcGFyYW1ldGVyIGxpbWl0cy5cbiAgICovXG4gIGFzeW5jIGJ1bGtDcmVhdGUoaXRlbXM6IFRJbnNlcnRbXSwgdXNlcklkOiBzdHJpbmcpOiBQcm9taXNlPFRFbnRpdHlbXT4ge1xuICAgIGlmIChpdGVtcy5sZW5ndGggPT09IDApIHJldHVybiBbXTtcblxuICAgIGNvbnN0IENIVU5LX1NJWkUgPSAxMDA7XG4gICAgY29uc3Qgbm93ID0gbmV3IERhdGUoKTtcbiAgICBjb25zdCB1c2VyID0gdXNlcklkIHx8ICdzeXN0ZW0nO1xuXG4gICAgY29uc3QgcmVzdWx0cyA9IGF3YWl0IGRiLnRyYW5zYWN0aW9uKGFzeW5jICh0eCkgPT4ge1xuICAgICAgY29uc3QgYWxsQ3JlYXRlZDogVEVudGl0eVtdID0gW107XG5cbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgaXRlbXMubGVuZ3RoOyBpICs9IENIVU5LX1NJWkUpIHtcbiAgICAgICAgY29uc3QgY2h1bmsgPSBpdGVtcy5zbGljZShpLCBpICsgQ0hVTktfU0laRSk7XG4gICAgICAgIGNvbnN0IHZhbHVlcyA9IGNodW5rLm1hcChkYXRhID0+ICh7XG4gICAgICAgICAgLi4uZGF0YSxcbiAgICAgICAgICBjcmVhdGVkQnk6IHVzZXIsXG4gICAgICAgICAgdXBkYXRlZEJ5OiB1c2VyLFxuICAgICAgICB9IGFzIGFueSkpO1xuXG4gICAgICAgIGNvbnN0IGNyZWF0ZWQgPSBhd2FpdCB0eFxuICAgICAgICAgIC5pbnNlcnQodGhpcy5zY2hlbWEpXG4gICAgICAgICAgLnZhbHVlcyh2YWx1ZXMpXG4gICAgICAgICAgLm9uQ29uZmxpY3REb1VwZGF0ZSh7XG4gICAgICAgICAgICB0YXJnZXQ6IHRoaXMuY29uZmxpY3RUYXJnZXQgYXMgYW55LFxuICAgICAgICAgICAgc2V0OiB7XG4gICAgICAgICAgICAgIHVwZGF0ZWRBdDogbm93LFxuICAgICAgICAgICAgICB1cGRhdGVkQnk6IHVzZXIsXG4gICAgICAgICAgICB9IGFzIGFueSxcbiAgICAgICAgICB9KVxuICAgICAgICAgIC5yZXR1cm5pbmcoKS50aGVuKHIgPT4gZHJpenpsZVJvd3M8VEVudGl0eT4ocikpO1xuXG4gICAgICAgIGFsbENyZWF0ZWQucHVzaCguLi5jcmVhdGVkKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIGFsbENyZWF0ZWQ7XG4gICAgfSk7XG5cbiAgICBmb3IgKGNvbnN0IGVudGl0eSBvZiByZXN1bHRzKSB7XG4gICAgICB0aGlzLm9uQWZ0ZXJDcmVhdGUoZW50aXR5LCB1c2VySWQpLmNhdGNoKGVyciA9PiB0aGlzLl9sb2dnZXIud2FybignTGlmZWN5Y2xlIGhvb2sgZmFpbGVkJywgeyBlcnJvcjogU3RyaW5nKGVycikgfSkpO1xuICAgIH1cblxuICAgIHJldHVybiByZXN1bHRzO1xuICB9XG5cbiAgLyoqXG4gICAqIFNvZnQtZGVsZXRlIG11bHRpcGxlIGVudGl0aWVzIGJ5IElEcyBpbiBhIHNpbmdsZSBiYXRjaCBvcGVyYXRpb24uXG4gICAqL1xuICBhc3luYyBidWxrRGVsZXRlKFxuICAgIGlkczogc3RyaW5nW10sXG4gICAgb3JnSWQ6IHN0cmluZyxcbiAgICB1c2VySWQ6IHN0cmluZyxcbiAgKTogUHJvbWlzZTxURW50aXR5W10+IHtcbiAgICBpZiAoaWRzLmxlbmd0aCA9PT0gMCkgcmV0dXJuIFtdO1xuXG4gICAgY29uc3Qgbm93ID0gbmV3IERhdGUoKTtcbiAgICBjb25zdCB1c2VyID0gdXNlcklkIHx8ICdzeXN0ZW0nO1xuICAgIGNvbnN0IGNvbmRpdGlvbnMgPSBbXG4gICAgICBpbkFycmF5KCh0aGlzLnNjaGVtYSBhcyBhbnkpLmlkLCBpZHMpLFxuICAgICAgLi4udGhpcy5idWlsZENvbmRpdGlvbnMoe30gYXMgUGFydGlhbDxURmlsdGVyPiwgb3JnSWQpLFxuICAgIF07XG5cbiAgICBjb25zdCBkZWxldGVkID0gYXdhaXQgZGJcbiAgICAgIC51cGRhdGUodGhpcy5zY2hlbWEpXG4gICAgICAuc2V0KHtcbiAgICAgICAgaXNBY3RpdmU6IGZhbHNlLFxuICAgICAgICB1cGRhdGVkQXQ6IG5vdyxcbiAgICAgICAgdXBkYXRlZEJ5OiB1c2VyLFxuICAgICAgICBkZWxldGVkQXQ6IG5vdyxcbiAgICAgICAgZGVsZXRlZEJ5OiB1c2VyLFxuICAgICAgfSBhcyBhbnkpXG4gICAgICAud2hlcmUoYW5kKC4uLmNvbmRpdGlvbnMpKVxuICAgICAgLnJldHVybmluZygpLnRoZW4ociA9PiBkcml6emxlUm93czxURW50aXR5PihyKSk7XG5cbiAgICBmb3IgKGNvbnN0IGVudGl0eSBvZiBkZWxldGVkKSB7XG4gICAgICB0aGlzLm9uQWZ0ZXJEZWxldGUoZW50aXR5LmlkLCBlbnRpdHksIHVzZXJJZCkuY2F0Y2goZXJyID0+IHRoaXMuX2xvZ2dlci53YXJuKCdMaWZlY3ljbGUgaG9vayBmYWlsZWQnLCB7IGVycm9yOiBTdHJpbmcoZXJyKSB9KSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGRlbGV0ZWQ7XG4gIH1cbn1cbiJdfQ==
|