@unified-product-graph/core 0.10.3 → 0.10.5
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 +98 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -31
- package/dist/index.d.ts +71 -31
- package/dist/index.js +95 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -24,6 +24,7 @@ __export(index_exports, {
|
|
|
24
24
|
BUSINESS_GROWTH_METRIC_DRIVEN_PLAYBOOK: () => BUSINESS_GROWTH_METRIC_DRIVEN_PLAYBOOK,
|
|
25
25
|
BUSINESS_GTM_GROWTH_PLAYBOOK: () => BUSINESS_GTM_GROWTH_PLAYBOOK,
|
|
26
26
|
BUSINESS_MARKETING_AUDIENCE_FIRST_PLAYBOOK: () => BUSINESS_MARKETING_AUDIENCE_FIRST_PLAYBOOK,
|
|
27
|
+
CLASSIFICATION_EDGE_PROPERTY_SCHEMA: () => CLASSIFICATION_EDGE_PROPERTY_SCHEMA,
|
|
27
28
|
CONTENT_DEPTH_WARNING_RULES: () => CONTENT_DEPTH_WARNING_RULES,
|
|
28
29
|
DEFAULT_ENTITY_EMOJI: () => DEFAULT_ENTITY_EMOJI,
|
|
29
30
|
DISCOVERY_RESEARCH_VALIDATION_PLAYBOOK: () => DISCOVERY_RESEARCH_VALIDATION_PLAYBOOK,
|
|
@@ -135,6 +136,7 @@ __export(index_exports, {
|
|
|
135
136
|
getDomainForType: () => getDomainForType,
|
|
136
137
|
getDomainIdForType: () => getDomainIdForType,
|
|
137
138
|
getDomainsInRing: () => getDomainsInRing,
|
|
139
|
+
getEdgePropertySchema: () => getEdgePropertySchema,
|
|
138
140
|
getEnumScale: () => getEnumScale,
|
|
139
141
|
getEnumValueMeta: () => getEnumValueMeta,
|
|
140
142
|
getGuideForDomain: () => getGuideForDomain,
|
|
@@ -199,6 +201,7 @@ __export(index_exports, {
|
|
|
199
201
|
ringOrderedDomainIds: () => ringOrderedDomainIds,
|
|
200
202
|
rotateSlug: () => rotateSlug,
|
|
201
203
|
serializeCanonical: () => serializeCanonical,
|
|
204
|
+
validateEdgeProperties: () => validateEdgeProperties,
|
|
202
205
|
validateProductStageStrict: () => validateProductStageStrict,
|
|
203
206
|
validateUPGDocument: () => validateUPGDocument,
|
|
204
207
|
validateUPGFramework: () => validateUPGFramework,
|
|
@@ -1185,6 +1188,32 @@ function getTypeName(typeId) {
|
|
|
1185
1188
|
|
|
1186
1189
|
// src/catalog/edge-catalog.ts
|
|
1187
1190
|
var UPG_WILDCARD_ENDPOINT = "node";
|
|
1191
|
+
var CLASSIFICATION_EDGE_PROPERTY_SCHEMA = {
|
|
1192
|
+
confidence: {
|
|
1193
|
+
type: "assessment",
|
|
1194
|
+
scale_id: "confidence_5",
|
|
1195
|
+
description: "How sure we are this node belongs in this classification cell, on the canonical confidence_5 scale. Numeric value plus a high/medium/low label.",
|
|
1196
|
+
properties: {
|
|
1197
|
+
value: { type: "number", description: "Numeric value 1-5 (confidence_5)." },
|
|
1198
|
+
label: { type: "string", description: "Qualitative label (Guessing/Hunch/Some evidence/Confident/Data-backed, or high/medium/low)." },
|
|
1199
|
+
scale_id: { type: "string", description: "Scale rated on (optional; defaults confidence_5)." },
|
|
1200
|
+
normalized: { type: "number", description: "Normalized 0-1 value for cross-tool comparison (optional)." }
|
|
1201
|
+
},
|
|
1202
|
+
required: ["value", "label"]
|
|
1203
|
+
},
|
|
1204
|
+
assessed_on: {
|
|
1205
|
+
type: "string",
|
|
1206
|
+
description: 'Provenance: ISO date-time the classification was made or last re-checked. Drives staleness queries. @example "2026-06-13"'
|
|
1207
|
+
},
|
|
1208
|
+
rationale: {
|
|
1209
|
+
type: "string",
|
|
1210
|
+
description: "Optional. Short note on why this node sits in this cell. Edge-level, distinct from classification_value.rationale (why the value exists as a category)."
|
|
1211
|
+
},
|
|
1212
|
+
evidence: {
|
|
1213
|
+
type: "string",
|
|
1214
|
+
description: "Optional. A source URL, or a competitor_signal / evidence node id backing the classification. Mirrors feature_rivals_competitor_feature.evidence (free text or a node id)."
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1188
1217
|
var UPG_EDGE_CATALOG = {
|
|
1189
1218
|
// ── Part 1: Core Narrative Spine (Ring 1) ──────────────────────────────────
|
|
1190
1219
|
// 1.1 User Domain
|
|
@@ -1357,13 +1386,13 @@ var UPG_EDGE_CATALOG = {
|
|
|
1357
1386
|
// competitor can be classified directly against a `registry/{classification_value}`
|
|
1358
1387
|
// canonical, eliminating the per-graph taxonomy node. Within-graph (the catalogue
|
|
1359
1388
|
// case) here; cross-product against the registry canonical via create_cross_product_edge.
|
|
1360
|
-
competitor_classified_as_classification_value: { forward_verb: "classified_as", reverse_verb: "classification_of", classification: "semantic", source_type: "competitor", target_type: "classification_value" },
|
|
1389
|
+
competitor_classified_as_classification_value: { forward_verb: "classified_as", reverse_verb: "classification_of", classification: "semantic", source_type: "competitor", target_type: "classification_value", carries_properties: true, property_schema: CLASSIFICATION_EDGE_PROPERTY_SCHEMA },
|
|
1361
1390
|
// 0.10.3: the polymorphic, type-agnostic sibling. ANY node (a feature, a
|
|
1362
1391
|
// product, a market_segment) classified against a classification_value, so
|
|
1363
1392
|
// classification is not welded to the competitor type. Also dual-registered as
|
|
1364
1393
|
// a cross-edge for the registry-canonical case. source_type:'node' (wildcard);
|
|
1365
1394
|
// listed in UPG_POLYMORPHIC_EDGE_KEYS.
|
|
1366
|
-
node_classified_as_classification_value: { forward_verb: "classified_as", reverse_verb: "classification_of", classification: "semantic", source_type: "node", target_type: "classification_value" },
|
|
1395
|
+
node_classified_as_classification_value: { forward_verb: "classified_as", reverse_verb: "classification_of", classification: "semantic", source_type: "node", target_type: "classification_value", carries_properties: true, property_schema: CLASSIFICATION_EDGE_PROPERTY_SCHEMA },
|
|
1367
1396
|
persona_anti_fit_for_classification_value: { forward_verb: "is_anti_fit_for", reverse_verb: "should_not_be_picked_by", classification: "semantic", source_type: "persona", target_type: "classification_value" },
|
|
1368
1397
|
persona_anti_fit_for_product: { forward_verb: "is_anti_fit_for", reverse_verb: "should_not_be_picked_by", classification: "semantic", source_type: "persona", target_type: "product" },
|
|
1369
1398
|
persona_anti_fit_for_competitor: { forward_verb: "is_anti_fit_for", reverse_verb: "should_not_be_picked_by", classification: "semantic", source_type: "persona", target_type: "competitor" },
|
|
@@ -3217,6 +3246,10 @@ function edgeCarriesProperties(type) {
|
|
|
3217
3246
|
const def = UPG_EDGE_CATALOG[type];
|
|
3218
3247
|
return def?.carries_properties === true;
|
|
3219
3248
|
}
|
|
3249
|
+
function getEdgePropertySchema(type) {
|
|
3250
|
+
const def = UPG_EDGE_CATALOG[type];
|
|
3251
|
+
return def?.property_schema;
|
|
3252
|
+
}
|
|
3220
3253
|
|
|
3221
3254
|
// src/catalog/legacy-product-stages.ts
|
|
3222
3255
|
var LEGACY_PRODUCT_STAGES = Object.freeze({
|
|
@@ -18747,6 +18780,65 @@ function isUPGDocument(doc) {
|
|
|
18747
18780
|
return validateUPGDocument(doc).valid;
|
|
18748
18781
|
}
|
|
18749
18782
|
|
|
18783
|
+
// src/properties/edge-property-validation.ts
|
|
18784
|
+
function validateEdgeProperties(edgeType, properties) {
|
|
18785
|
+
const schema = getEdgePropertySchema(edgeType);
|
|
18786
|
+
if (!schema || !properties) return [];
|
|
18787
|
+
const errors = [];
|
|
18788
|
+
const allowed = Object.keys(schema);
|
|
18789
|
+
for (const key of Object.keys(properties)) {
|
|
18790
|
+
if (!allowed.includes(key)) {
|
|
18791
|
+
errors.push(`unknown property "${key}" for edge type "${edgeType}" (allowed: ${allowed.join(", ")})`);
|
|
18792
|
+
}
|
|
18793
|
+
}
|
|
18794
|
+
for (const [key, def] of Object.entries(schema)) {
|
|
18795
|
+
const val = properties[key];
|
|
18796
|
+
if (val === void 0 || val === null) continue;
|
|
18797
|
+
errors.push(...validateValue(key, def, val));
|
|
18798
|
+
}
|
|
18799
|
+
return errors;
|
|
18800
|
+
}
|
|
18801
|
+
function validateValue(key, def, val) {
|
|
18802
|
+
const errors = [];
|
|
18803
|
+
switch (def.type) {
|
|
18804
|
+
case "string":
|
|
18805
|
+
if (typeof val !== "string") errors.push(`property "${key}" must be a string`);
|
|
18806
|
+
break;
|
|
18807
|
+
case "number":
|
|
18808
|
+
if (typeof val !== "number") errors.push(`property "${key}" must be a number`);
|
|
18809
|
+
break;
|
|
18810
|
+
case "boolean":
|
|
18811
|
+
if (typeof val !== "boolean") errors.push(`property "${key}" must be a boolean`);
|
|
18812
|
+
break;
|
|
18813
|
+
case "assessment": {
|
|
18814
|
+
if (typeof val !== "object" || Array.isArray(val)) {
|
|
18815
|
+
errors.push(`property "${key}" must be an assessment object with value and label`);
|
|
18816
|
+
break;
|
|
18817
|
+
}
|
|
18818
|
+
const a = val;
|
|
18819
|
+
for (const r of def.required ?? ["value", "label"]) {
|
|
18820
|
+
if (a[r] === void 0 || a[r] === null) errors.push(`assessment "${key}" is missing required "${r}"`);
|
|
18821
|
+
}
|
|
18822
|
+
if (a.value !== void 0 && a.value !== null) {
|
|
18823
|
+
if (typeof a.value !== "number") {
|
|
18824
|
+
errors.push(`assessment "${key}".value must be a number`);
|
|
18825
|
+
} else {
|
|
18826
|
+
const scaleId = (typeof a.scale_id === "string" ? a.scale_id : void 0) ?? def.scale_id;
|
|
18827
|
+
const scale = scaleId ? getScale(scaleId) : void 0;
|
|
18828
|
+
if (scale && (a.value < scale.min || a.value > scale.max)) {
|
|
18829
|
+
errors.push(`assessment "${key}".value ${a.value} is out of range ${scale.min}-${scale.max} for scale ${scaleId}`);
|
|
18830
|
+
}
|
|
18831
|
+
}
|
|
18832
|
+
}
|
|
18833
|
+
if (typeof a.scale_id === "string" && def.scale_id !== void 0 && a.scale_id !== def.scale_id) {
|
|
18834
|
+
errors.push(`assessment "${key}".scale_id "${a.scale_id}" must be "${def.scale_id}"`);
|
|
18835
|
+
}
|
|
18836
|
+
break;
|
|
18837
|
+
}
|
|
18838
|
+
}
|
|
18839
|
+
return errors;
|
|
18840
|
+
}
|
|
18841
|
+
|
|
18750
18842
|
// src/presentation/entity-emoji.ts
|
|
18751
18843
|
var DEFAULT_ENTITY_EMOJI = "\u{1F4E6}";
|
|
18752
18844
|
var ENTITY_EMOJI = {
|
|
@@ -28646,7 +28738,7 @@ function isCanonical(text) {
|
|
|
28646
28738
|
}
|
|
28647
28739
|
|
|
28648
28740
|
// src/index.ts
|
|
28649
|
-
var UPG_VERSION = "0.10.
|
|
28741
|
+
var UPG_VERSION = "0.10.5";
|
|
28650
28742
|
var UPG_FORMAT_VERSION = "0.4.0";
|
|
28651
28743
|
var MARKDOWN_FORMAT_VERSION = "0.1";
|
|
28652
28744
|
var UPG_TYPES = getTypes();
|
|
@@ -28715,6 +28807,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
|
28715
28807
|
BUSINESS_GROWTH_METRIC_DRIVEN_PLAYBOOK,
|
|
28716
28808
|
BUSINESS_GTM_GROWTH_PLAYBOOK,
|
|
28717
28809
|
BUSINESS_MARKETING_AUDIENCE_FIRST_PLAYBOOK,
|
|
28810
|
+
CLASSIFICATION_EDGE_PROPERTY_SCHEMA,
|
|
28718
28811
|
CONTENT_DEPTH_WARNING_RULES,
|
|
28719
28812
|
DEFAULT_ENTITY_EMOJI,
|
|
28720
28813
|
DISCOVERY_RESEARCH_VALIDATION_PLAYBOOK,
|
|
@@ -28826,6 +28919,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
|
28826
28919
|
getDomainForType,
|
|
28827
28920
|
getDomainIdForType,
|
|
28828
28921
|
getDomainsInRing,
|
|
28922
|
+
getEdgePropertySchema,
|
|
28829
28923
|
getEnumScale,
|
|
28830
28924
|
getEnumValueMeta,
|
|
28831
28925
|
getGuideForDomain,
|
|
@@ -28890,6 +28984,7 @@ var UPG_META_COUNT = UPG_ENTITY_META.length;
|
|
|
28890
28984
|
ringOrderedDomainIds,
|
|
28891
28985
|
rotateSlug,
|
|
28892
28986
|
serializeCanonical,
|
|
28987
|
+
validateEdgeProperties,
|
|
28893
28988
|
validateProductStageStrict,
|
|
28894
28989
|
validateUPGDocument,
|
|
28895
28990
|
validateUPGFramework,
|