@pgpmjs/core 5.0.0 → 5.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/slice/slice.js ADDED
@@ -0,0 +1,536 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildDependencyGraph = buildDependencyGraph;
4
+ exports.validateDAG = validateDAG;
5
+ exports.extractPackageFromPath = extractPackageFromPath;
6
+ exports.findMatchingPattern = findMatchingPattern;
7
+ exports.assignChangesToPackages = assignChangesToPackages;
8
+ exports.mergeSmallPackages = mergeSmallPackages;
9
+ exports.buildPackageDependencies = buildPackageDependencies;
10
+ exports.detectPackageCycle = detectPackageCycle;
11
+ exports.computeDeployOrder = computeDeployOrder;
12
+ exports.topologicalSortWithinPackage = topologicalSortWithinPackage;
13
+ exports.generatePlanContent = generatePlanContent;
14
+ exports.generateControlContent = generateControlContent;
15
+ exports.generateSinglePackage = generateSinglePackage;
16
+ exports.slicePlan = slicePlan;
17
+ const parser_1 = require("../files/plan/parser");
18
+ const minimatch_1 = require("minimatch");
19
+ /**
20
+ * Build a dependency graph from a parsed plan file
21
+ */
22
+ function buildDependencyGraph(plan) {
23
+ const graph = {
24
+ nodes: new Map(),
25
+ edges: new Map(),
26
+ reverseEdges: new Map(),
27
+ tags: new Map(),
28
+ plan
29
+ };
30
+ // Add all changes as nodes
31
+ for (const change of plan.changes) {
32
+ graph.nodes.set(change.name, change);
33
+ graph.edges.set(change.name, new Set(change.dependencies || []));
34
+ // Build reverse edges for dependency analysis
35
+ for (const dep of change.dependencies || []) {
36
+ if (!graph.reverseEdges.has(dep)) {
37
+ graph.reverseEdges.set(dep, new Set());
38
+ }
39
+ graph.reverseEdges.get(dep).add(change.name);
40
+ }
41
+ }
42
+ // Index tags by change
43
+ for (const tag of plan.tags || []) {
44
+ if (!graph.tags.has(tag.change)) {
45
+ graph.tags.set(tag.change, []);
46
+ }
47
+ graph.tags.get(tag.change).push(tag);
48
+ }
49
+ return graph;
50
+ }
51
+ /**
52
+ * Validate that the dependency graph is a DAG (no cycles)
53
+ */
54
+ function validateDAG(graph) {
55
+ const visited = new Set();
56
+ const visiting = new Set();
57
+ function dfs(node, path) {
58
+ if (visiting.has(node)) {
59
+ throw new Error(`Cycle detected: ${[...path, node].join(' -> ')}`);
60
+ }
61
+ if (visited.has(node))
62
+ return;
63
+ visiting.add(node);
64
+ const deps = graph.edges.get(node) || new Set();
65
+ for (const dep of deps) {
66
+ // Only follow internal dependencies
67
+ if (graph.nodes.has(dep)) {
68
+ dfs(dep, [...path, node]);
69
+ }
70
+ }
71
+ visiting.delete(node);
72
+ visited.add(node);
73
+ }
74
+ for (const node of graph.nodes.keys()) {
75
+ dfs(node, []);
76
+ }
77
+ }
78
+ /**
79
+ * Extract package name from a change path using folder-based strategy
80
+ */
81
+ function extractPackageFromPath(changeName, depth = 1, prefixToStrip = 'schemas') {
82
+ const parts = changeName.split('/');
83
+ // Handle special folders that should go to core
84
+ if (parts[0] === 'extensions' || parts[0] === 'migrate') {
85
+ return 'core';
86
+ }
87
+ // Strip prefix if present
88
+ let startIdx = 0;
89
+ if (parts[0] === prefixToStrip) {
90
+ startIdx = 1;
91
+ }
92
+ // Get package name at specified depth
93
+ if (parts.length > startIdx) {
94
+ // For depth > 1, join multiple segments
95
+ const endIdx = Math.min(startIdx + depth, parts.length - 1);
96
+ if (endIdx > startIdx) {
97
+ return parts.slice(startIdx, endIdx).join('/');
98
+ }
99
+ return parts[startIdx];
100
+ }
101
+ return 'core';
102
+ }
103
+ /**
104
+ * Find the matching package for a change using pattern-based strategy.
105
+ * Returns the first matching slice's package name, or undefined if no match.
106
+ */
107
+ function findMatchingPattern(changeName, strategy) {
108
+ for (const slice of strategy.slices) {
109
+ for (const pattern of slice.patterns) {
110
+ if ((0, minimatch_1.minimatch)(changeName, pattern, { dot: true })) {
111
+ return slice.packageName;
112
+ }
113
+ }
114
+ }
115
+ return undefined;
116
+ }
117
+ /**
118
+ * Assign changes to packages based on grouping strategy
119
+ */
120
+ function assignChangesToPackages(graph, strategy, defaultPackage = 'core') {
121
+ const assignments = new Map();
122
+ for (const [changeName] of graph.nodes) {
123
+ let packageName;
124
+ switch (strategy.type) {
125
+ case 'folder': {
126
+ const depth = strategy.depth ?? 1;
127
+ const prefix = strategy.prefixToStrip ?? 'schemas';
128
+ packageName = extractPackageFromPath(changeName, depth, prefix);
129
+ break;
130
+ }
131
+ case 'pattern': {
132
+ const matched = findMatchingPattern(changeName, strategy);
133
+ packageName = matched || defaultPackage;
134
+ break;
135
+ }
136
+ case 'explicit': {
137
+ packageName = strategy.mapping[changeName] || defaultPackage;
138
+ break;
139
+ }
140
+ default:
141
+ packageName = defaultPackage;
142
+ }
143
+ // Fallback to default package
144
+ if (!packageName) {
145
+ packageName = defaultPackage;
146
+ }
147
+ if (!assignments.has(packageName)) {
148
+ assignments.set(packageName, new Set());
149
+ }
150
+ assignments.get(packageName).add(changeName);
151
+ }
152
+ return assignments;
153
+ }
154
+ /**
155
+ * Merge small packages into larger ones
156
+ */
157
+ function mergeSmallPackages(assignments, minChanges, defaultPackage = 'core') {
158
+ const result = new Map();
159
+ // Ensure default package exists
160
+ if (!result.has(defaultPackage)) {
161
+ result.set(defaultPackage, new Set());
162
+ }
163
+ for (const [pkg, changes] of assignments) {
164
+ if (changes.size < minChanges && pkg !== defaultPackage) {
165
+ // Merge into default package
166
+ const defaultChanges = result.get(defaultPackage);
167
+ for (const change of changes) {
168
+ defaultChanges.add(change);
169
+ }
170
+ }
171
+ else {
172
+ result.set(pkg, new Set(changes));
173
+ }
174
+ }
175
+ // Remove empty default package if it exists
176
+ if (result.get(defaultPackage)?.size === 0) {
177
+ result.delete(defaultPackage);
178
+ }
179
+ return result;
180
+ }
181
+ /**
182
+ * Build package-level dependency graph
183
+ */
184
+ function buildPackageDependencies(graph, assignments) {
185
+ const packageDeps = new Map();
186
+ // Build reverse lookup: change -> package
187
+ const changeToPackage = new Map();
188
+ for (const [pkg, changes] of assignments) {
189
+ packageDeps.set(pkg, new Set());
190
+ for (const change of changes) {
191
+ changeToPackage.set(change, pkg);
192
+ }
193
+ }
194
+ // Check each change's dependencies
195
+ for (const [changeName, deps] of graph.edges) {
196
+ const myPackage = changeToPackage.get(changeName);
197
+ if (!myPackage)
198
+ continue;
199
+ for (const dep of deps) {
200
+ const depPackage = changeToPackage.get(dep);
201
+ if (depPackage && depPackage !== myPackage) {
202
+ // Cross-package dependency
203
+ packageDeps.get(myPackage).add(depPackage);
204
+ }
205
+ }
206
+ }
207
+ return packageDeps;
208
+ }
209
+ /**
210
+ * Detect cycles in package dependency graph
211
+ */
212
+ function detectPackageCycle(deps) {
213
+ const visited = new Set();
214
+ const visiting = new Set();
215
+ function dfs(pkg, path) {
216
+ if (visiting.has(pkg)) {
217
+ return [...path, pkg];
218
+ }
219
+ if (visited.has(pkg))
220
+ return null;
221
+ visiting.add(pkg);
222
+ const pkgDeps = deps.get(pkg) || new Set();
223
+ for (const dep of pkgDeps) {
224
+ const cycle = dfs(dep, [...path, pkg]);
225
+ if (cycle)
226
+ return cycle;
227
+ }
228
+ visiting.delete(pkg);
229
+ visited.add(pkg);
230
+ return null;
231
+ }
232
+ for (const pkg of deps.keys()) {
233
+ const cycle = dfs(pkg, []);
234
+ if (cycle)
235
+ return cycle;
236
+ }
237
+ return null;
238
+ }
239
+ /**
240
+ * Compute deployment order for packages (topological sort)
241
+ */
242
+ function computeDeployOrder(packageDeps) {
243
+ const order = [];
244
+ const visited = new Set();
245
+ const allPackages = new Set();
246
+ // Collect all packages
247
+ for (const pkg of packageDeps.keys()) {
248
+ allPackages.add(pkg);
249
+ }
250
+ for (const deps of packageDeps.values()) {
251
+ for (const dep of deps) {
252
+ allPackages.add(dep);
253
+ }
254
+ }
255
+ function visit(pkg) {
256
+ if (visited.has(pkg))
257
+ return;
258
+ visited.add(pkg);
259
+ const deps = packageDeps.get(pkg) || new Set();
260
+ for (const dep of deps) {
261
+ visit(dep);
262
+ }
263
+ order.push(pkg);
264
+ }
265
+ // Visit all packages (sorted for determinism)
266
+ for (const pkg of [...allPackages].sort()) {
267
+ visit(pkg);
268
+ }
269
+ return order;
270
+ }
271
+ /**
272
+ * Topological sort of changes within a package
273
+ */
274
+ function topologicalSortWithinPackage(changes, graph) {
275
+ const result = [];
276
+ const visited = new Set();
277
+ function visit(change) {
278
+ if (visited.has(change))
279
+ return;
280
+ if (!changes.has(change))
281
+ return;
282
+ visited.add(change);
283
+ const deps = graph.edges.get(change) || new Set();
284
+ for (const dep of deps) {
285
+ if (changes.has(dep)) {
286
+ visit(dep);
287
+ }
288
+ }
289
+ result.push(change);
290
+ }
291
+ // Visit in alphabetical order for determinism
292
+ for (const change of [...changes].sort()) {
293
+ visit(change);
294
+ }
295
+ return result;
296
+ }
297
+ /**
298
+ * Generate plan file content for a package
299
+ */
300
+ function generatePlanContent(pkgName, entries, tags = []) {
301
+ let content = `%syntax-version=1.0.0\n`;
302
+ content += `%project=${pkgName}\n`;
303
+ content += `%uri=${pkgName}\n\n`;
304
+ for (const entry of entries) {
305
+ let line = entry.name;
306
+ if (entry.dependencies && entry.dependencies.length > 0) {
307
+ line += ` [${entry.dependencies.join(' ')}]`;
308
+ }
309
+ if (entry.timestamp) {
310
+ line += ` ${entry.timestamp}`;
311
+ if (entry.planner) {
312
+ line += ` ${entry.planner}`;
313
+ if (entry.email) {
314
+ line += ` <${entry.email}>`;
315
+ }
316
+ }
317
+ }
318
+ if (entry.comment) {
319
+ line += ` # ${entry.comment}`;
320
+ }
321
+ content += line + '\n';
322
+ // Add tags associated with this change
323
+ const changeTags = tags.filter(t => t.change === entry.name);
324
+ for (const tag of changeTags) {
325
+ let tagLine = `@${tag.name}`;
326
+ if (tag.timestamp) {
327
+ tagLine += ` ${tag.timestamp}`;
328
+ if (tag.planner) {
329
+ tagLine += ` ${tag.planner}`;
330
+ if (tag.email) {
331
+ tagLine += ` <${tag.email}>`;
332
+ }
333
+ }
334
+ }
335
+ if (tag.comment) {
336
+ tagLine += ` # ${tag.comment}`;
337
+ }
338
+ content += tagLine + '\n';
339
+ }
340
+ }
341
+ return content;
342
+ }
343
+ /**
344
+ * Generate control file content for a package
345
+ */
346
+ function generateControlContent(pkgName, deps) {
347
+ let content = `# ${pkgName} extension\n`;
348
+ content += `comment = '${pkgName} module'\n`;
349
+ content += `default_version = '0.0.1'\n`;
350
+ content += `relocatable = false\n`;
351
+ if (deps.size > 0) {
352
+ content += `requires = '${[...deps].sort().join(', ')}'\n`;
353
+ }
354
+ return content;
355
+ }
356
+ /**
357
+ * Generate a single package output
358
+ */
359
+ function generateSinglePackage(pkgName, changes, graph, changeToPackage, pkgDeps, useTagsForCrossPackageDeps = false) {
360
+ // Sort changes in topological order within package
361
+ const sortedChanges = topologicalSortWithinPackage(changes, graph);
362
+ // Build plan entries with updated dependencies
363
+ const planEntries = [];
364
+ for (const changeName of sortedChanges) {
365
+ const originalChange = graph.nodes.get(changeName);
366
+ const deps = graph.edges.get(changeName) || new Set();
367
+ const newDeps = [];
368
+ for (const dep of deps) {
369
+ const depPkg = changeToPackage.get(dep);
370
+ if (!depPkg) {
371
+ // External dependency (from installed module) - keep as-is
372
+ newDeps.push(dep);
373
+ }
374
+ else if (depPkg === pkgName) {
375
+ // Internal dependency - keep as-is
376
+ newDeps.push(dep);
377
+ }
378
+ else {
379
+ // Cross-package dependency
380
+ if (useTagsForCrossPackageDeps) {
381
+ // Find latest tag in the dependency package
382
+ const depPkgChanges = [...graph.nodes.keys()]
383
+ .filter(c => changeToPackage.get(c) === depPkg);
384
+ const lastChange = depPkgChanges[depPkgChanges.length - 1];
385
+ const tags = graph.tags.get(lastChange);
386
+ if (tags && tags.length > 0) {
387
+ const latestTag = tags[tags.length - 1];
388
+ newDeps.push(`${depPkg}:@${latestTag.name}`);
389
+ }
390
+ else {
391
+ newDeps.push(`${depPkg}:${dep}`);
392
+ }
393
+ }
394
+ else {
395
+ newDeps.push(`${depPkg}:${dep}`);
396
+ }
397
+ }
398
+ }
399
+ planEntries.push({
400
+ name: changeName,
401
+ dependencies: newDeps,
402
+ timestamp: originalChange.timestamp,
403
+ planner: originalChange.planner,
404
+ email: originalChange.email,
405
+ comment: originalChange.comment
406
+ });
407
+ }
408
+ // Collect tags for this package
409
+ const packageTags = [];
410
+ for (const changeName of sortedChanges) {
411
+ const changeTags = graph.tags.get(changeName) || [];
412
+ packageTags.push(...changeTags);
413
+ }
414
+ // Generate plan content
415
+ const planContent = generatePlanContent(pkgName, planEntries, packageTags);
416
+ // Generate control file content
417
+ const controlContent = generateControlContent(pkgName, pkgDeps);
418
+ return {
419
+ name: pkgName,
420
+ planContent,
421
+ controlContent,
422
+ changes: planEntries,
423
+ packageDependencies: [...pkgDeps]
424
+ };
425
+ }
426
+ /**
427
+ * Main slicing function
428
+ */
429
+ function slicePlan(config) {
430
+ // Parse the source plan file
431
+ const planResult = (0, parser_1.parsePlanFile)(config.sourcePlan);
432
+ if (!planResult.data) {
433
+ const errorMessages = planResult.errors?.map(e => `Line ${e.line}: ${e.message}`).join('\n') || 'Unknown error';
434
+ throw new Error(`Failed to parse plan file: ${errorMessages}`);
435
+ }
436
+ const plan = planResult.data;
437
+ const warnings = [];
438
+ // Build dependency graph
439
+ const graph = buildDependencyGraph(plan);
440
+ // Validate DAG
441
+ try {
442
+ validateDAG(graph);
443
+ }
444
+ catch (error) {
445
+ throw new Error(`Invalid plan file: ${error.message}`);
446
+ }
447
+ // Assign changes to packages
448
+ let assignments = assignChangesToPackages(graph, config.strategy, config.defaultPackage || 'core');
449
+ // Merge small packages if configured
450
+ if (config.minChangesPerPackage && config.minChangesPerPackage > 1) {
451
+ assignments = mergeSmallPackages(assignments, config.minChangesPerPackage, config.defaultPackage || 'core');
452
+ }
453
+ // Build package dependencies
454
+ const packageDeps = buildPackageDependencies(graph, assignments);
455
+ // Check for package cycles
456
+ const cycle = detectPackageCycle(packageDeps);
457
+ if (cycle) {
458
+ warnings.push({
459
+ type: 'cycle_detected',
460
+ message: `Package cycle detected: ${cycle.join(' -> ')}`,
461
+ suggestedAction: 'Consider merging these packages or reorganizing dependencies'
462
+ });
463
+ // For now, we'll proceed but warn - in production we might want to auto-merge
464
+ }
465
+ // Build reverse lookup
466
+ const changeToPackage = new Map();
467
+ for (const [pkg, changes] of assignments) {
468
+ for (const change of changes) {
469
+ changeToPackage.set(change, pkg);
470
+ }
471
+ }
472
+ // Compute deploy order
473
+ const deployOrder = computeDeployOrder(packageDeps);
474
+ // Generate packages
475
+ const packages = [];
476
+ for (const pkgName of deployOrder) {
477
+ const changes = assignments.get(pkgName);
478
+ if (!changes || changes.size === 0)
479
+ continue;
480
+ const pkgOutput = generateSinglePackage(pkgName, changes, graph, changeToPackage, packageDeps.get(pkgName) || new Set(), config.useTagsForCrossPackageDeps || false);
481
+ packages.push(pkgOutput);
482
+ // Check for heavy cross-package dependencies
483
+ let crossDeps = 0;
484
+ let totalDeps = 0;
485
+ for (const change of changes) {
486
+ const deps = graph.edges.get(change) || new Set();
487
+ for (const dep of deps) {
488
+ totalDeps++;
489
+ const depPkg = changeToPackage.get(dep);
490
+ if (depPkg && depPkg !== pkgName) {
491
+ crossDeps++;
492
+ }
493
+ }
494
+ }
495
+ const ratio = totalDeps > 0 ? crossDeps / totalDeps : 0;
496
+ if (ratio > 0.5) {
497
+ warnings.push({
498
+ type: 'heavy_cross_deps',
499
+ message: `Package "${pkgName}" has ${Math.round(ratio * 100)}% cross-package dependencies`,
500
+ suggestedAction: 'Consider merging with dependent packages or reorganizing'
501
+ });
502
+ }
503
+ }
504
+ // Calculate stats
505
+ let internalEdges = 0;
506
+ let crossPackageEdges = 0;
507
+ for (const [change, deps] of graph.edges) {
508
+ const myPkg = changeToPackage.get(change);
509
+ for (const dep of deps) {
510
+ const depPkg = changeToPackage.get(dep);
511
+ if (depPkg === myPkg) {
512
+ internalEdges++;
513
+ }
514
+ else if (depPkg) {
515
+ crossPackageEdges++;
516
+ }
517
+ }
518
+ }
519
+ const totalEdges = internalEdges + crossPackageEdges;
520
+ return {
521
+ packages,
522
+ workspace: {
523
+ packages: deployOrder,
524
+ deployOrder,
525
+ dependencies: Object.fromEntries([...packageDeps.entries()].map(([k, v]) => [k, [...v].sort()]))
526
+ },
527
+ warnings,
528
+ stats: {
529
+ totalChanges: graph.nodes.size,
530
+ packagesCreated: packages.length,
531
+ internalEdges,
532
+ crossPackageEdges,
533
+ crossPackageRatio: totalEdges > 0 ? crossPackageEdges / totalEdges : 0
534
+ }
535
+ };
536
+ }
@@ -0,0 +1,133 @@
1
+ import { Change, Tag, ExtendedPlanFile } from '../files/types';
2
+ /**
3
+ * Configuration for slicing a plan into multiple packages
4
+ */
5
+ export interface SliceConfig {
6
+ /** Source plan file path */
7
+ sourcePlan: string;
8
+ /** Output directory for packages */
9
+ outputDir: string;
10
+ /** Grouping strategy */
11
+ strategy: GroupingStrategy;
12
+ /** Package for changes that don't match any group */
13
+ defaultPackage?: string;
14
+ /** Whether to use tags for cross-package deps */
15
+ useTagsForCrossPackageDeps?: boolean;
16
+ /** Minimum changes per package (merge smaller groups) */
17
+ minChangesPerPackage?: number;
18
+ /** Author for generated plan files */
19
+ author?: string;
20
+ }
21
+ /**
22
+ * Grouping strategy for slicing
23
+ */
24
+ export type GroupingStrategy = FolderStrategy | PatternStrategy | ExplicitStrategy;
25
+ export interface FolderStrategy {
26
+ type: 'folder';
27
+ /** Depth in path to extract package name (default: 1) */
28
+ depth?: number;
29
+ /** Prefix to strip from paths (default: 'schemas') */
30
+ prefixToStrip?: string;
31
+ }
32
+ /**
33
+ * Pattern-based strategy using glob patterns to match changes to packages.
34
+ * Each slice defines a package name and an array of glob patterns.
35
+ * Changes matching any pattern in a slice are assigned to that package.
36
+ */
37
+ export interface PatternStrategy {
38
+ type: 'pattern';
39
+ /** Array of slice definitions with package names and patterns */
40
+ slices: PatternSlice[];
41
+ }
42
+ /**
43
+ * A single slice definition for pattern-based grouping
44
+ */
45
+ export interface PatternSlice {
46
+ /** Name of the output package */
47
+ packageName: string;
48
+ /** Glob patterns to match change paths (e.g., "schemas/auth/**") */
49
+ patterns: string[];
50
+ }
51
+ export interface ExplicitStrategy {
52
+ type: 'explicit';
53
+ /** Mapping of change name to package name */
54
+ mapping: Record<string, string>;
55
+ }
56
+ /**
57
+ * Dependency graph representation
58
+ */
59
+ export interface DependencyGraph {
60
+ /** Map of change name to Change object */
61
+ nodes: Map<string, Change>;
62
+ /** Map of change name to its dependencies */
63
+ edges: Map<string, Set<string>>;
64
+ /** Map of change name to changes that depend on it */
65
+ reverseEdges: Map<string, Set<string>>;
66
+ /** Map of change name to its tags */
67
+ tags: Map<string, Tag[]>;
68
+ /** Original plan metadata */
69
+ plan: ExtendedPlanFile;
70
+ }
71
+ /**
72
+ * Result of slicing operation
73
+ */
74
+ export interface SliceResult {
75
+ /** Generated packages */
76
+ packages: PackageOutput[];
77
+ /** Workspace manifest */
78
+ workspace: WorkspaceManifest;
79
+ /** Warnings/issues encountered */
80
+ warnings: SliceWarning[];
81
+ /** Statistics */
82
+ stats: SliceStats;
83
+ }
84
+ /**
85
+ * Output for a single package
86
+ */
87
+ export interface PackageOutput {
88
+ /** Package name */
89
+ name: string;
90
+ /** Plan file content */
91
+ planContent: string;
92
+ /** Control file content */
93
+ controlContent: string;
94
+ /** Changes in this package */
95
+ changes: Change[];
96
+ /** Dependencies on other packages */
97
+ packageDependencies: string[];
98
+ }
99
+ /**
100
+ * Workspace manifest describing package relationships
101
+ */
102
+ export interface WorkspaceManifest {
103
+ /** List of package names */
104
+ packages: string[];
105
+ /** Order in which packages should be deployed */
106
+ deployOrder: string[];
107
+ /** Map of package name to its package dependencies */
108
+ dependencies: Record<string, string[]>;
109
+ }
110
+ /**
111
+ * Warning generated during slicing
112
+ */
113
+ export interface SliceWarning {
114
+ type: 'heavy_cross_deps' | 'cycle_detected' | 'orphan_change' | 'merge_required';
115
+ message: string;
116
+ affectedChanges?: string[];
117
+ suggestedAction?: string;
118
+ }
119
+ /**
120
+ * Statistics about the slicing operation
121
+ */
122
+ export interface SliceStats {
123
+ /** Total number of changes processed */
124
+ totalChanges: number;
125
+ /** Number of packages created */
126
+ packagesCreated: number;
127
+ /** Number of internal dependency edges */
128
+ internalEdges: number;
129
+ /** Number of cross-package dependency edges */
130
+ crossPackageEdges: number;
131
+ /** Ratio of cross-package to total edges */
132
+ crossPackageRatio: number;
133
+ }
package/slice/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });