@pgpmjs/core 5.0.1 → 5.1.1
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/esm/export/export-meta.js +22 -11
- package/esm/export/export-migrations.js +1 -1
- package/esm/index.js +1 -0
- package/esm/slice/index.js +3 -0
- package/esm/slice/output.js +117 -0
- package/esm/slice/slice.js +520 -0
- package/esm/slice/types.js +1 -0
- package/export/export-meta.js +22 -11
- package/export/export-migrations.js +1 -1
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +3 -2
- package/slice/index.d.ts +3 -0
- package/slice/index.js +19 -0
- package/slice/output.d.ts +22 -0
- package/slice/output.js +124 -0
- package/slice/slice.d.ts +59 -0
- package/slice/slice.js +536 -0
- package/slice/types.d.ts +133 -0
- package/slice/types.js +2 -0
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
import { parsePlanFile } from '../files/plan/parser';
|
|
2
|
+
import { minimatch } from 'minimatch';
|
|
3
|
+
/**
|
|
4
|
+
* Build a dependency graph from a parsed plan file
|
|
5
|
+
*/
|
|
6
|
+
export function buildDependencyGraph(plan) {
|
|
7
|
+
const graph = {
|
|
8
|
+
nodes: new Map(),
|
|
9
|
+
edges: new Map(),
|
|
10
|
+
reverseEdges: new Map(),
|
|
11
|
+
tags: new Map(),
|
|
12
|
+
plan
|
|
13
|
+
};
|
|
14
|
+
// Add all changes as nodes
|
|
15
|
+
for (const change of plan.changes) {
|
|
16
|
+
graph.nodes.set(change.name, change);
|
|
17
|
+
graph.edges.set(change.name, new Set(change.dependencies || []));
|
|
18
|
+
// Build reverse edges for dependency analysis
|
|
19
|
+
for (const dep of change.dependencies || []) {
|
|
20
|
+
if (!graph.reverseEdges.has(dep)) {
|
|
21
|
+
graph.reverseEdges.set(dep, new Set());
|
|
22
|
+
}
|
|
23
|
+
graph.reverseEdges.get(dep).add(change.name);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Index tags by change
|
|
27
|
+
for (const tag of plan.tags || []) {
|
|
28
|
+
if (!graph.tags.has(tag.change)) {
|
|
29
|
+
graph.tags.set(tag.change, []);
|
|
30
|
+
}
|
|
31
|
+
graph.tags.get(tag.change).push(tag);
|
|
32
|
+
}
|
|
33
|
+
return graph;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Validate that the dependency graph is a DAG (no cycles)
|
|
37
|
+
*/
|
|
38
|
+
export function validateDAG(graph) {
|
|
39
|
+
const visited = new Set();
|
|
40
|
+
const visiting = new Set();
|
|
41
|
+
function dfs(node, path) {
|
|
42
|
+
if (visiting.has(node)) {
|
|
43
|
+
throw new Error(`Cycle detected: ${[...path, node].join(' -> ')}`);
|
|
44
|
+
}
|
|
45
|
+
if (visited.has(node))
|
|
46
|
+
return;
|
|
47
|
+
visiting.add(node);
|
|
48
|
+
const deps = graph.edges.get(node) || new Set();
|
|
49
|
+
for (const dep of deps) {
|
|
50
|
+
// Only follow internal dependencies
|
|
51
|
+
if (graph.nodes.has(dep)) {
|
|
52
|
+
dfs(dep, [...path, node]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
visiting.delete(node);
|
|
56
|
+
visited.add(node);
|
|
57
|
+
}
|
|
58
|
+
for (const node of graph.nodes.keys()) {
|
|
59
|
+
dfs(node, []);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Extract package name from a change path using folder-based strategy
|
|
64
|
+
*/
|
|
65
|
+
export function extractPackageFromPath(changeName, depth = 1, prefixToStrip = 'schemas') {
|
|
66
|
+
const parts = changeName.split('/');
|
|
67
|
+
// Handle special folders that should go to core
|
|
68
|
+
if (parts[0] === 'extensions' || parts[0] === 'migrate') {
|
|
69
|
+
return 'core';
|
|
70
|
+
}
|
|
71
|
+
// Strip prefix if present
|
|
72
|
+
let startIdx = 0;
|
|
73
|
+
if (parts[0] === prefixToStrip) {
|
|
74
|
+
startIdx = 1;
|
|
75
|
+
}
|
|
76
|
+
// Get package name at specified depth
|
|
77
|
+
if (parts.length > startIdx) {
|
|
78
|
+
// For depth > 1, join multiple segments
|
|
79
|
+
const endIdx = Math.min(startIdx + depth, parts.length - 1);
|
|
80
|
+
if (endIdx > startIdx) {
|
|
81
|
+
return parts.slice(startIdx, endIdx).join('/');
|
|
82
|
+
}
|
|
83
|
+
return parts[startIdx];
|
|
84
|
+
}
|
|
85
|
+
return 'core';
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Find the matching package for a change using pattern-based strategy.
|
|
89
|
+
* Returns the first matching slice's package name, or undefined if no match.
|
|
90
|
+
*/
|
|
91
|
+
export function findMatchingPattern(changeName, strategy) {
|
|
92
|
+
for (const slice of strategy.slices) {
|
|
93
|
+
for (const pattern of slice.patterns) {
|
|
94
|
+
if (minimatch(changeName, pattern, { dot: true })) {
|
|
95
|
+
return slice.packageName;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Assign changes to packages based on grouping strategy
|
|
103
|
+
*/
|
|
104
|
+
export function assignChangesToPackages(graph, strategy, defaultPackage = 'core') {
|
|
105
|
+
const assignments = new Map();
|
|
106
|
+
for (const [changeName] of graph.nodes) {
|
|
107
|
+
let packageName;
|
|
108
|
+
switch (strategy.type) {
|
|
109
|
+
case 'folder': {
|
|
110
|
+
const depth = strategy.depth ?? 1;
|
|
111
|
+
const prefix = strategy.prefixToStrip ?? 'schemas';
|
|
112
|
+
packageName = extractPackageFromPath(changeName, depth, prefix);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
case 'pattern': {
|
|
116
|
+
const matched = findMatchingPattern(changeName, strategy);
|
|
117
|
+
packageName = matched || defaultPackage;
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
case 'explicit': {
|
|
121
|
+
packageName = strategy.mapping[changeName] || defaultPackage;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
default:
|
|
125
|
+
packageName = defaultPackage;
|
|
126
|
+
}
|
|
127
|
+
// Fallback to default package
|
|
128
|
+
if (!packageName) {
|
|
129
|
+
packageName = defaultPackage;
|
|
130
|
+
}
|
|
131
|
+
if (!assignments.has(packageName)) {
|
|
132
|
+
assignments.set(packageName, new Set());
|
|
133
|
+
}
|
|
134
|
+
assignments.get(packageName).add(changeName);
|
|
135
|
+
}
|
|
136
|
+
return assignments;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Merge small packages into larger ones
|
|
140
|
+
*/
|
|
141
|
+
export function mergeSmallPackages(assignments, minChanges, defaultPackage = 'core') {
|
|
142
|
+
const result = new Map();
|
|
143
|
+
// Ensure default package exists
|
|
144
|
+
if (!result.has(defaultPackage)) {
|
|
145
|
+
result.set(defaultPackage, new Set());
|
|
146
|
+
}
|
|
147
|
+
for (const [pkg, changes] of assignments) {
|
|
148
|
+
if (changes.size < minChanges && pkg !== defaultPackage) {
|
|
149
|
+
// Merge into default package
|
|
150
|
+
const defaultChanges = result.get(defaultPackage);
|
|
151
|
+
for (const change of changes) {
|
|
152
|
+
defaultChanges.add(change);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
result.set(pkg, new Set(changes));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Remove empty default package if it exists
|
|
160
|
+
if (result.get(defaultPackage)?.size === 0) {
|
|
161
|
+
result.delete(defaultPackage);
|
|
162
|
+
}
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Build package-level dependency graph
|
|
167
|
+
*/
|
|
168
|
+
export function buildPackageDependencies(graph, assignments) {
|
|
169
|
+
const packageDeps = new Map();
|
|
170
|
+
// Build reverse lookup: change -> package
|
|
171
|
+
const changeToPackage = new Map();
|
|
172
|
+
for (const [pkg, changes] of assignments) {
|
|
173
|
+
packageDeps.set(pkg, new Set());
|
|
174
|
+
for (const change of changes) {
|
|
175
|
+
changeToPackage.set(change, pkg);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Check each change's dependencies
|
|
179
|
+
for (const [changeName, deps] of graph.edges) {
|
|
180
|
+
const myPackage = changeToPackage.get(changeName);
|
|
181
|
+
if (!myPackage)
|
|
182
|
+
continue;
|
|
183
|
+
for (const dep of deps) {
|
|
184
|
+
const depPackage = changeToPackage.get(dep);
|
|
185
|
+
if (depPackage && depPackage !== myPackage) {
|
|
186
|
+
// Cross-package dependency
|
|
187
|
+
packageDeps.get(myPackage).add(depPackage);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return packageDeps;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Detect cycles in package dependency graph
|
|
195
|
+
*/
|
|
196
|
+
export function detectPackageCycle(deps) {
|
|
197
|
+
const visited = new Set();
|
|
198
|
+
const visiting = new Set();
|
|
199
|
+
function dfs(pkg, path) {
|
|
200
|
+
if (visiting.has(pkg)) {
|
|
201
|
+
return [...path, pkg];
|
|
202
|
+
}
|
|
203
|
+
if (visited.has(pkg))
|
|
204
|
+
return null;
|
|
205
|
+
visiting.add(pkg);
|
|
206
|
+
const pkgDeps = deps.get(pkg) || new Set();
|
|
207
|
+
for (const dep of pkgDeps) {
|
|
208
|
+
const cycle = dfs(dep, [...path, pkg]);
|
|
209
|
+
if (cycle)
|
|
210
|
+
return cycle;
|
|
211
|
+
}
|
|
212
|
+
visiting.delete(pkg);
|
|
213
|
+
visited.add(pkg);
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
for (const pkg of deps.keys()) {
|
|
217
|
+
const cycle = dfs(pkg, []);
|
|
218
|
+
if (cycle)
|
|
219
|
+
return cycle;
|
|
220
|
+
}
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Compute deployment order for packages (topological sort)
|
|
225
|
+
*/
|
|
226
|
+
export function computeDeployOrder(packageDeps) {
|
|
227
|
+
const order = [];
|
|
228
|
+
const visited = new Set();
|
|
229
|
+
const allPackages = new Set();
|
|
230
|
+
// Collect all packages
|
|
231
|
+
for (const pkg of packageDeps.keys()) {
|
|
232
|
+
allPackages.add(pkg);
|
|
233
|
+
}
|
|
234
|
+
for (const deps of packageDeps.values()) {
|
|
235
|
+
for (const dep of deps) {
|
|
236
|
+
allPackages.add(dep);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function visit(pkg) {
|
|
240
|
+
if (visited.has(pkg))
|
|
241
|
+
return;
|
|
242
|
+
visited.add(pkg);
|
|
243
|
+
const deps = packageDeps.get(pkg) || new Set();
|
|
244
|
+
for (const dep of deps) {
|
|
245
|
+
visit(dep);
|
|
246
|
+
}
|
|
247
|
+
order.push(pkg);
|
|
248
|
+
}
|
|
249
|
+
// Visit all packages (sorted for determinism)
|
|
250
|
+
for (const pkg of [...allPackages].sort()) {
|
|
251
|
+
visit(pkg);
|
|
252
|
+
}
|
|
253
|
+
return order;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Topological sort of changes within a package
|
|
257
|
+
*/
|
|
258
|
+
export function topologicalSortWithinPackage(changes, graph) {
|
|
259
|
+
const result = [];
|
|
260
|
+
const visited = new Set();
|
|
261
|
+
function visit(change) {
|
|
262
|
+
if (visited.has(change))
|
|
263
|
+
return;
|
|
264
|
+
if (!changes.has(change))
|
|
265
|
+
return;
|
|
266
|
+
visited.add(change);
|
|
267
|
+
const deps = graph.edges.get(change) || new Set();
|
|
268
|
+
for (const dep of deps) {
|
|
269
|
+
if (changes.has(dep)) {
|
|
270
|
+
visit(dep);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
result.push(change);
|
|
274
|
+
}
|
|
275
|
+
// Visit in alphabetical order for determinism
|
|
276
|
+
for (const change of [...changes].sort()) {
|
|
277
|
+
visit(change);
|
|
278
|
+
}
|
|
279
|
+
return result;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Generate plan file content for a package
|
|
283
|
+
*/
|
|
284
|
+
export function generatePlanContent(pkgName, entries, tags = []) {
|
|
285
|
+
let content = `%syntax-version=1.0.0\n`;
|
|
286
|
+
content += `%project=${pkgName}\n`;
|
|
287
|
+
content += `%uri=${pkgName}\n\n`;
|
|
288
|
+
for (const entry of entries) {
|
|
289
|
+
let line = entry.name;
|
|
290
|
+
if (entry.dependencies && entry.dependencies.length > 0) {
|
|
291
|
+
line += ` [${entry.dependencies.join(' ')}]`;
|
|
292
|
+
}
|
|
293
|
+
if (entry.timestamp) {
|
|
294
|
+
line += ` ${entry.timestamp}`;
|
|
295
|
+
if (entry.planner) {
|
|
296
|
+
line += ` ${entry.planner}`;
|
|
297
|
+
if (entry.email) {
|
|
298
|
+
line += ` <${entry.email}>`;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (entry.comment) {
|
|
303
|
+
line += ` # ${entry.comment}`;
|
|
304
|
+
}
|
|
305
|
+
content += line + '\n';
|
|
306
|
+
// Add tags associated with this change
|
|
307
|
+
const changeTags = tags.filter(t => t.change === entry.name);
|
|
308
|
+
for (const tag of changeTags) {
|
|
309
|
+
let tagLine = `@${tag.name}`;
|
|
310
|
+
if (tag.timestamp) {
|
|
311
|
+
tagLine += ` ${tag.timestamp}`;
|
|
312
|
+
if (tag.planner) {
|
|
313
|
+
tagLine += ` ${tag.planner}`;
|
|
314
|
+
if (tag.email) {
|
|
315
|
+
tagLine += ` <${tag.email}>`;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (tag.comment) {
|
|
320
|
+
tagLine += ` # ${tag.comment}`;
|
|
321
|
+
}
|
|
322
|
+
content += tagLine + '\n';
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return content;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Generate control file content for a package
|
|
329
|
+
*/
|
|
330
|
+
export function generateControlContent(pkgName, deps) {
|
|
331
|
+
let content = `# ${pkgName} extension\n`;
|
|
332
|
+
content += `comment = '${pkgName} module'\n`;
|
|
333
|
+
content += `default_version = '0.0.1'\n`;
|
|
334
|
+
content += `relocatable = false\n`;
|
|
335
|
+
if (deps.size > 0) {
|
|
336
|
+
content += `requires = '${[...deps].sort().join(', ')}'\n`;
|
|
337
|
+
}
|
|
338
|
+
return content;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Generate a single package output
|
|
342
|
+
*/
|
|
343
|
+
export function generateSinglePackage(pkgName, changes, graph, changeToPackage, pkgDeps, useTagsForCrossPackageDeps = false) {
|
|
344
|
+
// Sort changes in topological order within package
|
|
345
|
+
const sortedChanges = topologicalSortWithinPackage(changes, graph);
|
|
346
|
+
// Build plan entries with updated dependencies
|
|
347
|
+
const planEntries = [];
|
|
348
|
+
for (const changeName of sortedChanges) {
|
|
349
|
+
const originalChange = graph.nodes.get(changeName);
|
|
350
|
+
const deps = graph.edges.get(changeName) || new Set();
|
|
351
|
+
const newDeps = [];
|
|
352
|
+
for (const dep of deps) {
|
|
353
|
+
const depPkg = changeToPackage.get(dep);
|
|
354
|
+
if (!depPkg) {
|
|
355
|
+
// External dependency (from installed module) - keep as-is
|
|
356
|
+
newDeps.push(dep);
|
|
357
|
+
}
|
|
358
|
+
else if (depPkg === pkgName) {
|
|
359
|
+
// Internal dependency - keep as-is
|
|
360
|
+
newDeps.push(dep);
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
// Cross-package dependency
|
|
364
|
+
if (useTagsForCrossPackageDeps) {
|
|
365
|
+
// Find latest tag in the dependency package
|
|
366
|
+
const depPkgChanges = [...graph.nodes.keys()]
|
|
367
|
+
.filter(c => changeToPackage.get(c) === depPkg);
|
|
368
|
+
const lastChange = depPkgChanges[depPkgChanges.length - 1];
|
|
369
|
+
const tags = graph.tags.get(lastChange);
|
|
370
|
+
if (tags && tags.length > 0) {
|
|
371
|
+
const latestTag = tags[tags.length - 1];
|
|
372
|
+
newDeps.push(`${depPkg}:@${latestTag.name}`);
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
newDeps.push(`${depPkg}:${dep}`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
newDeps.push(`${depPkg}:${dep}`);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
planEntries.push({
|
|
384
|
+
name: changeName,
|
|
385
|
+
dependencies: newDeps,
|
|
386
|
+
timestamp: originalChange.timestamp,
|
|
387
|
+
planner: originalChange.planner,
|
|
388
|
+
email: originalChange.email,
|
|
389
|
+
comment: originalChange.comment
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
// Collect tags for this package
|
|
393
|
+
const packageTags = [];
|
|
394
|
+
for (const changeName of sortedChanges) {
|
|
395
|
+
const changeTags = graph.tags.get(changeName) || [];
|
|
396
|
+
packageTags.push(...changeTags);
|
|
397
|
+
}
|
|
398
|
+
// Generate plan content
|
|
399
|
+
const planContent = generatePlanContent(pkgName, planEntries, packageTags);
|
|
400
|
+
// Generate control file content
|
|
401
|
+
const controlContent = generateControlContent(pkgName, pkgDeps);
|
|
402
|
+
return {
|
|
403
|
+
name: pkgName,
|
|
404
|
+
planContent,
|
|
405
|
+
controlContent,
|
|
406
|
+
changes: planEntries,
|
|
407
|
+
packageDependencies: [...pkgDeps]
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Main slicing function
|
|
412
|
+
*/
|
|
413
|
+
export function slicePlan(config) {
|
|
414
|
+
// Parse the source plan file
|
|
415
|
+
const planResult = parsePlanFile(config.sourcePlan);
|
|
416
|
+
if (!planResult.data) {
|
|
417
|
+
const errorMessages = planResult.errors?.map(e => `Line ${e.line}: ${e.message}`).join('\n') || 'Unknown error';
|
|
418
|
+
throw new Error(`Failed to parse plan file: ${errorMessages}`);
|
|
419
|
+
}
|
|
420
|
+
const plan = planResult.data;
|
|
421
|
+
const warnings = [];
|
|
422
|
+
// Build dependency graph
|
|
423
|
+
const graph = buildDependencyGraph(plan);
|
|
424
|
+
// Validate DAG
|
|
425
|
+
try {
|
|
426
|
+
validateDAG(graph);
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
throw new Error(`Invalid plan file: ${error.message}`);
|
|
430
|
+
}
|
|
431
|
+
// Assign changes to packages
|
|
432
|
+
let assignments = assignChangesToPackages(graph, config.strategy, config.defaultPackage || 'core');
|
|
433
|
+
// Merge small packages if configured
|
|
434
|
+
if (config.minChangesPerPackage && config.minChangesPerPackage > 1) {
|
|
435
|
+
assignments = mergeSmallPackages(assignments, config.minChangesPerPackage, config.defaultPackage || 'core');
|
|
436
|
+
}
|
|
437
|
+
// Build package dependencies
|
|
438
|
+
const packageDeps = buildPackageDependencies(graph, assignments);
|
|
439
|
+
// Check for package cycles
|
|
440
|
+
const cycle = detectPackageCycle(packageDeps);
|
|
441
|
+
if (cycle) {
|
|
442
|
+
warnings.push({
|
|
443
|
+
type: 'cycle_detected',
|
|
444
|
+
message: `Package cycle detected: ${cycle.join(' -> ')}`,
|
|
445
|
+
suggestedAction: 'Consider merging these packages or reorganizing dependencies'
|
|
446
|
+
});
|
|
447
|
+
// For now, we'll proceed but warn - in production we might want to auto-merge
|
|
448
|
+
}
|
|
449
|
+
// Build reverse lookup
|
|
450
|
+
const changeToPackage = new Map();
|
|
451
|
+
for (const [pkg, changes] of assignments) {
|
|
452
|
+
for (const change of changes) {
|
|
453
|
+
changeToPackage.set(change, pkg);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
// Compute deploy order
|
|
457
|
+
const deployOrder = computeDeployOrder(packageDeps);
|
|
458
|
+
// Generate packages
|
|
459
|
+
const packages = [];
|
|
460
|
+
for (const pkgName of deployOrder) {
|
|
461
|
+
const changes = assignments.get(pkgName);
|
|
462
|
+
if (!changes || changes.size === 0)
|
|
463
|
+
continue;
|
|
464
|
+
const pkgOutput = generateSinglePackage(pkgName, changes, graph, changeToPackage, packageDeps.get(pkgName) || new Set(), config.useTagsForCrossPackageDeps || false);
|
|
465
|
+
packages.push(pkgOutput);
|
|
466
|
+
// Check for heavy cross-package dependencies
|
|
467
|
+
let crossDeps = 0;
|
|
468
|
+
let totalDeps = 0;
|
|
469
|
+
for (const change of changes) {
|
|
470
|
+
const deps = graph.edges.get(change) || new Set();
|
|
471
|
+
for (const dep of deps) {
|
|
472
|
+
totalDeps++;
|
|
473
|
+
const depPkg = changeToPackage.get(dep);
|
|
474
|
+
if (depPkg && depPkg !== pkgName) {
|
|
475
|
+
crossDeps++;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
const ratio = totalDeps > 0 ? crossDeps / totalDeps : 0;
|
|
480
|
+
if (ratio > 0.5) {
|
|
481
|
+
warnings.push({
|
|
482
|
+
type: 'heavy_cross_deps',
|
|
483
|
+
message: `Package "${pkgName}" has ${Math.round(ratio * 100)}% cross-package dependencies`,
|
|
484
|
+
suggestedAction: 'Consider merging with dependent packages or reorganizing'
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
// Calculate stats
|
|
489
|
+
let internalEdges = 0;
|
|
490
|
+
let crossPackageEdges = 0;
|
|
491
|
+
for (const [change, deps] of graph.edges) {
|
|
492
|
+
const myPkg = changeToPackage.get(change);
|
|
493
|
+
for (const dep of deps) {
|
|
494
|
+
const depPkg = changeToPackage.get(dep);
|
|
495
|
+
if (depPkg === myPkg) {
|
|
496
|
+
internalEdges++;
|
|
497
|
+
}
|
|
498
|
+
else if (depPkg) {
|
|
499
|
+
crossPackageEdges++;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
const totalEdges = internalEdges + crossPackageEdges;
|
|
504
|
+
return {
|
|
505
|
+
packages,
|
|
506
|
+
workspace: {
|
|
507
|
+
packages: deployOrder,
|
|
508
|
+
deployOrder,
|
|
509
|
+
dependencies: Object.fromEntries([...packageDeps.entries()].map(([k, v]) => [k, [...v].sort()]))
|
|
510
|
+
},
|
|
511
|
+
warnings,
|
|
512
|
+
stats: {
|
|
513
|
+
totalChanges: graph.nodes.size,
|
|
514
|
+
packagesCreated: packages.length,
|
|
515
|
+
internalEdges,
|
|
516
|
+
crossPackageEdges,
|
|
517
|
+
crossPackageRatio: totalEdges > 0 ? crossPackageEdges / totalEdges : 0
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/export/export-meta.js
CHANGED
|
@@ -446,7 +446,8 @@ const config = {
|
|
|
446
446
|
api_id: 'uuid',
|
|
447
447
|
schema_id: 'uuid',
|
|
448
448
|
private_schema_id: 'uuid',
|
|
449
|
-
|
|
449
|
+
session_credentials_table_id: 'uuid',
|
|
450
|
+
sessions_table_id: 'uuid',
|
|
450
451
|
users_table_id: 'uuid',
|
|
451
452
|
authenticate: 'text',
|
|
452
453
|
authenticate_strict: 'text',
|
|
@@ -465,7 +466,10 @@ const config = {
|
|
|
465
466
|
users_table_id: 'uuid',
|
|
466
467
|
secrets_table_id: 'uuid',
|
|
467
468
|
encrypted_table_id: 'uuid',
|
|
468
|
-
|
|
469
|
+
sessions_table_id: 'uuid',
|
|
470
|
+
session_credentials_table_id: 'uuid',
|
|
471
|
+
audits_table_id: 'uuid',
|
|
472
|
+
audits_table_name: 'text',
|
|
469
473
|
sign_in_function: 'text',
|
|
470
474
|
sign_up_function: 'text',
|
|
471
475
|
sign_out_function: 'text',
|
|
@@ -478,7 +482,9 @@ const config = {
|
|
|
478
482
|
reset_password_function: 'text',
|
|
479
483
|
forgot_password_function: 'text',
|
|
480
484
|
send_verification_email_function: 'text',
|
|
481
|
-
verify_email_function: 'text'
|
|
485
|
+
verify_email_function: 'text',
|
|
486
|
+
verify_password_function: 'text',
|
|
487
|
+
check_password_function: 'text'
|
|
482
488
|
}
|
|
483
489
|
},
|
|
484
490
|
memberships_module: {
|
|
@@ -680,17 +686,21 @@ const config = {
|
|
|
680
686
|
table_name: 'text'
|
|
681
687
|
}
|
|
682
688
|
},
|
|
683
|
-
|
|
689
|
+
sessions_module: {
|
|
684
690
|
schema: 'metaschema_modules_public',
|
|
685
|
-
table: '
|
|
691
|
+
table: 'sessions_module',
|
|
686
692
|
fields: {
|
|
687
693
|
id: 'uuid',
|
|
688
694
|
database_id: 'uuid',
|
|
689
695
|
schema_id: 'uuid',
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
696
|
+
sessions_table_id: 'uuid',
|
|
697
|
+
session_credentials_table_id: 'uuid',
|
|
698
|
+
auth_settings_table_id: 'uuid',
|
|
699
|
+
users_table_id: 'uuid',
|
|
700
|
+
sessions_default_expiration: 'interval',
|
|
701
|
+
sessions_table: 'text',
|
|
702
|
+
session_credentials_table: 'text',
|
|
703
|
+
auth_settings_table: 'text'
|
|
694
704
|
}
|
|
695
705
|
},
|
|
696
706
|
secrets_module: {
|
|
@@ -788,7 +798,8 @@ const config = {
|
|
|
788
798
|
database_id: 'uuid',
|
|
789
799
|
schema_id: 'uuid',
|
|
790
800
|
users_table_id: 'uuid',
|
|
791
|
-
|
|
801
|
+
sessions_table_id: 'uuid',
|
|
802
|
+
session_credentials_table_id: 'uuid',
|
|
792
803
|
secrets_table_id: 'uuid',
|
|
793
804
|
addresses_table_id: 'uuid',
|
|
794
805
|
user_field: 'text',
|
|
@@ -983,7 +994,7 @@ const exportMeta = async ({ opts, dbname, database_id }) => {
|
|
|
983
994
|
await queryAndParse('membership_types_module', `SELECT * FROM metaschema_modules_public.membership_types_module WHERE database_id = $1`);
|
|
984
995
|
await queryAndParse('invites_module', `SELECT * FROM metaschema_modules_public.invites_module WHERE database_id = $1`);
|
|
985
996
|
await queryAndParse('emails_module', `SELECT * FROM metaschema_modules_public.emails_module WHERE database_id = $1`);
|
|
986
|
-
await queryAndParse('
|
|
997
|
+
await queryAndParse('sessions_module', `SELECT * FROM metaschema_modules_public.sessions_module WHERE database_id = $1`);
|
|
987
998
|
await queryAndParse('secrets_module', `SELECT * FROM metaschema_modules_public.secrets_module WHERE database_id = $1`);
|
|
988
999
|
await queryAndParse('profiles_module', `SELECT * FROM metaschema_modules_public.profiles_module WHERE database_id = $1`);
|
|
989
1000
|
await queryAndParse('encrypted_secrets_module', `SELECT * FROM metaschema_modules_public.encrypted_secrets_module WHERE database_id = $1`);
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -18,6 +18,7 @@ exports.withTransaction = exports.executeQuery = exports.hashString = exports.ha
|
|
|
18
18
|
__exportStar(require("./core/class/pgpm"), exports);
|
|
19
19
|
__exportStar(require("./export/export-meta"), exports);
|
|
20
20
|
__exportStar(require("./export/export-migrations"), exports);
|
|
21
|
+
__exportStar(require("./slice"), exports);
|
|
21
22
|
__exportStar(require("./extensions/extensions"), exports);
|
|
22
23
|
__exportStar(require("./modules/modules"), exports);
|
|
23
24
|
__exportStar(require("./packaging/package"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pgpmjs/core",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.1",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "PGPM Package and Migration Tools",
|
|
6
6
|
"main": "index.js",
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"genomic": "^5.3.0",
|
|
57
57
|
"glob": "^13.0.0",
|
|
58
58
|
"komoji": "^0.8.0",
|
|
59
|
+
"minimatch": "^10.1.1",
|
|
59
60
|
"parse-package-name": "^1.0.0",
|
|
60
61
|
"pg": "^8.17.1",
|
|
61
62
|
"pg-cache": "^2.0.0",
|
|
@@ -64,5 +65,5 @@
|
|
|
64
65
|
"pgsql-parser": "^17.9.11",
|
|
65
66
|
"yanse": "^0.2.0"
|
|
66
67
|
},
|
|
67
|
-
"gitHead": "
|
|
68
|
+
"gitHead": "27b059d8fb062729f697cd3d3415a57de28a0975"
|
|
68
69
|
}
|
package/slice/index.d.ts
ADDED
package/slice/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./types"), exports);
|
|
18
|
+
__exportStar(require("./slice"), exports);
|
|
19
|
+
__exportStar(require("./output"), exports);
|