@unified-product-graph/core 0.26.0 → 0.26.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/dist/index.cjs +87 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +85 -2
- package/dist/index.d.ts +85 -2
- package/dist/index.js +82 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -135,6 +135,8 @@ __export(index_exports, {
|
|
|
135
135
|
backfillSlug: () => backfillSlug,
|
|
136
136
|
buildTypeAliases: () => buildTypeAliases,
|
|
137
137
|
canBeChildOf: () => canBeChildOf,
|
|
138
|
+
checkHeaderSeal: () => checkHeaderSeal,
|
|
139
|
+
checkHeaderSealText: () => checkHeaderSealText,
|
|
138
140
|
coerceProductStage: () => coerceProductStage,
|
|
139
141
|
collectSlugsForType: () => collectSlugsForType,
|
|
140
142
|
compareVersions: () => compareVersions,
|
|
@@ -143,6 +145,9 @@ __export(index_exports, {
|
|
|
143
145
|
concernFor: () => concernFor,
|
|
144
146
|
concernGatesFor: () => concernGatesFor,
|
|
145
147
|
crossProductScope: () => crossProductScope,
|
|
148
|
+
deriveCounts: () => deriveCounts,
|
|
149
|
+
derivePortfolioCounts: () => derivePortfolioCounts,
|
|
150
|
+
deriveSingleCounts: () => deriveSingleCounts,
|
|
146
151
|
describeTreePattern: () => describeTreePattern,
|
|
147
152
|
edgeCarriesProperties: () => edgeCarriesProperties,
|
|
148
153
|
evaluateAntiPatterns: () => evaluateAntiPatterns,
|
|
@@ -30160,6 +30165,28 @@ function portfolioBody(doc) {
|
|
|
30160
30165
|
...doc.signals && doc.signals.length > 0 ? { signals: sortNodes(doc.signals).map(canonicalNode) } : {}
|
|
30161
30166
|
};
|
|
30162
30167
|
}
|
|
30168
|
+
function deriveSingleCounts(doc) {
|
|
30169
|
+
return { nodes: doc.nodes?.length ?? 0, edges: doc.edges?.length ?? 0 };
|
|
30170
|
+
}
|
|
30171
|
+
function derivePortfolioCounts(doc) {
|
|
30172
|
+
const members = doc.products ?? [];
|
|
30173
|
+
const memberKindOf = (p) => p.member_kind ?? "product";
|
|
30174
|
+
const watchedCount = members.filter((p) => memberKindOf(p) === "watched").length;
|
|
30175
|
+
const rollupCount = members.filter((p) => memberKindOf(p) === "org_rollup").length;
|
|
30176
|
+
const operatingFunctionCount = members.filter((p) => memberKindOf(p) === "operating_function").length;
|
|
30177
|
+
return {
|
|
30178
|
+
products: members.filter((p) => memberKindOf(p) === "product").length,
|
|
30179
|
+
...watchedCount > 0 ? { watched_products: watchedCount } : {},
|
|
30180
|
+
...rollupCount > 0 ? { org_rollups: rollupCount } : {},
|
|
30181
|
+
...operatingFunctionCount > 0 ? { operating_functions: operatingFunctionCount } : {},
|
|
30182
|
+
product_areas: doc.product_areas?.length ?? 0,
|
|
30183
|
+
portfolios: doc.portfolios?.length ?? 0,
|
|
30184
|
+
cross_edges: doc.cross_edges?.length ?? 0
|
|
30185
|
+
};
|
|
30186
|
+
}
|
|
30187
|
+
function deriveCounts(doc) {
|
|
30188
|
+
return isPortfolio(doc) ? derivePortfolioCounts(doc) : deriveSingleCounts(doc);
|
|
30189
|
+
}
|
|
30163
30190
|
function computeBodyChecksum(doc) {
|
|
30164
30191
|
const body = isPortfolio(doc) ? portfolioBody(doc) : singleBody(doc);
|
|
30165
30192
|
const content = JSON.stringify(body);
|
|
@@ -30199,7 +30226,7 @@ function serializeSingleWithHeader(doc, opts) {
|
|
|
30199
30226
|
};
|
|
30200
30227
|
const summary = deriveSummary(product.description);
|
|
30201
30228
|
if (summary) header.summary = summary;
|
|
30202
|
-
header.counts =
|
|
30229
|
+
header.counts = deriveSingleCounts(doc);
|
|
30203
30230
|
if (doc.member_kind === "org_rollup" || doc.member_kind === "watched" || doc.member_kind === "operating_function") {
|
|
30204
30231
|
header.member_kind = doc.member_kind;
|
|
30205
30232
|
}
|
|
@@ -30222,20 +30249,7 @@ function serializePortfolioWithHeader(doc, opts) {
|
|
|
30222
30249
|
};
|
|
30223
30250
|
const summary = deriveSummary(org.description);
|
|
30224
30251
|
if (summary) header.summary = summary;
|
|
30225
|
-
|
|
30226
|
-
const memberKindOf = (p) => p.member_kind ?? "product";
|
|
30227
|
-
const watchedCount = members.filter((p) => memberKindOf(p) === "watched").length;
|
|
30228
|
-
const rollupCount = members.filter((p) => memberKindOf(p) === "org_rollup").length;
|
|
30229
|
-
const operatingFunctionCount = members.filter((p) => memberKindOf(p) === "operating_function").length;
|
|
30230
|
-
header.counts = {
|
|
30231
|
-
products: members.filter((p) => memberKindOf(p) === "product").length,
|
|
30232
|
-
...watchedCount > 0 ? { watched_products: watchedCount } : {},
|
|
30233
|
-
...rollupCount > 0 ? { org_rollups: rollupCount } : {},
|
|
30234
|
-
...operatingFunctionCount > 0 ? { operating_functions: operatingFunctionCount } : {},
|
|
30235
|
-
product_areas: doc.product_areas?.length ?? 0,
|
|
30236
|
-
portfolios: doc.portfolios?.length ?? 0,
|
|
30237
|
-
cross_edges: doc.cross_edges?.length ?? 0
|
|
30238
|
-
};
|
|
30252
|
+
header.counts = derivePortfolioCounts(doc);
|
|
30239
30253
|
header.provenance = buildProvenance(doc, opts);
|
|
30240
30254
|
header.integrity = { algorithm: INTEGRITY_ALGORITHM, body: computeBodyChecksum(doc) };
|
|
30241
30255
|
return JSON.stringify({ $upg: header, ...body }, null, 2) + "\n";
|
|
@@ -30303,9 +30317,61 @@ function isCanonical(text) {
|
|
|
30303
30317
|
const parsed = parseUpg(text);
|
|
30304
30318
|
return text === serializeCanonical(parsed);
|
|
30305
30319
|
}
|
|
30320
|
+
function checkHeaderSeal(obj) {
|
|
30321
|
+
const raw = obj;
|
|
30322
|
+
const header = raw?.$upg;
|
|
30323
|
+
const report = {
|
|
30324
|
+
header_present: !!header,
|
|
30325
|
+
counts_checked: false,
|
|
30326
|
+
integrity_checked: false,
|
|
30327
|
+
counts_drift: [],
|
|
30328
|
+
integrity_drift: []
|
|
30329
|
+
};
|
|
30330
|
+
if (!header) return report;
|
|
30331
|
+
if (header.format_version !== UPG_CANONICAL_FORMAT_VERSION) {
|
|
30332
|
+
report.skipped_reason = `format_version "${header.format_version}" is not this serialiser's "${UPG_CANONICAL_FORMAT_VERSION}"; counts keys and body layout are format-version contracts, so neither seal can be judged here.`;
|
|
30333
|
+
return report;
|
|
30334
|
+
}
|
|
30335
|
+
const doc = normalizeDocument(raw);
|
|
30336
|
+
const declaredCounts = header.counts ?? {};
|
|
30337
|
+
const actualCounts = deriveCounts(doc);
|
|
30338
|
+
if (header.counts === void 0) {
|
|
30339
|
+
report.skipped_reason = "header has no `counts` block to compare.";
|
|
30340
|
+
} else {
|
|
30341
|
+
report.counts_checked = true;
|
|
30342
|
+
const fields = /* @__PURE__ */ new Set([...Object.keys(declaredCounts), ...Object.keys(actualCounts)]);
|
|
30343
|
+
for (const field of [...fields].sort()) {
|
|
30344
|
+
const declaredRaw = declaredCounts[field];
|
|
30345
|
+
const declared = typeof declaredRaw === "number" ? declaredRaw : 0;
|
|
30346
|
+
const actual = actualCounts[field] ?? 0;
|
|
30347
|
+
if (declared !== actual) report.counts_drift.push({ field, declared, actual });
|
|
30348
|
+
}
|
|
30349
|
+
}
|
|
30350
|
+
const integrity = header.integrity;
|
|
30351
|
+
if (!integrity || typeof integrity.body !== "string") {
|
|
30352
|
+
report.skipped_reason = report.skipped_reason ?? "header has no `integrity.body` seal to compare.";
|
|
30353
|
+
} else if (integrity.algorithm !== INTEGRITY_ALGORITHM) {
|
|
30354
|
+
report.skipped_reason = report.skipped_reason ?? `integrity.algorithm "${integrity.algorithm}" is not "${INTEGRITY_ALGORITHM}"; the declared digest cannot be recomputed here.`;
|
|
30355
|
+
} else {
|
|
30356
|
+
report.integrity_checked = true;
|
|
30357
|
+
const computed = computeBodyChecksum(doc);
|
|
30358
|
+
if (computed !== integrity.body) {
|
|
30359
|
+
report.integrity_drift.push({
|
|
30360
|
+
algorithm: integrity.algorithm,
|
|
30361
|
+
declared: integrity.body,
|
|
30362
|
+
computed
|
|
30363
|
+
});
|
|
30364
|
+
}
|
|
30365
|
+
}
|
|
30366
|
+
return report;
|
|
30367
|
+
}
|
|
30368
|
+
function checkHeaderSealText(text) {
|
|
30369
|
+
const noBom = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
|
|
30370
|
+
return checkHeaderSeal(JSON.parse(noBom));
|
|
30371
|
+
}
|
|
30306
30372
|
|
|
30307
30373
|
// src/index.ts
|
|
30308
|
-
var UPG_VERSION = "0.26.
|
|
30374
|
+
var UPG_VERSION = "0.26.1";
|
|
30309
30375
|
var UPG_FORMAT_VERSION = "0.4.0";
|
|
30310
30376
|
var MARKDOWN_FORMAT_VERSION = "0.1";
|
|
30311
30377
|
var UPG_TYPES = getTypes();
|
|
@@ -30475,6 +30541,8 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
|
30475
30541
|
backfillSlug,
|
|
30476
30542
|
buildTypeAliases,
|
|
30477
30543
|
canBeChildOf,
|
|
30544
|
+
checkHeaderSeal,
|
|
30545
|
+
checkHeaderSealText,
|
|
30478
30546
|
coerceProductStage,
|
|
30479
30547
|
collectSlugsForType,
|
|
30480
30548
|
compareVersions,
|
|
@@ -30483,6 +30551,9 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
|
30483
30551
|
concernFor,
|
|
30484
30552
|
concernGatesFor,
|
|
30485
30553
|
crossProductScope,
|
|
30554
|
+
deriveCounts,
|
|
30555
|
+
derivePortfolioCounts,
|
|
30556
|
+
deriveSingleCounts,
|
|
30486
30557
|
describeTreePattern,
|
|
30487
30558
|
edgeCarriesProperties,
|
|
30488
30559
|
evaluateAntiPatterns,
|