@principal-ai/principal-view-cli 0.3.8 → 0.4.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
CHANGED
|
@@ -8971,9 +8971,9 @@ var require_out4 = __commonJS({
|
|
|
8971
8971
|
}
|
|
8972
8972
|
});
|
|
8973
8973
|
|
|
8974
|
-
// node_modules/
|
|
8974
|
+
// node_modules/ignore/index.js
|
|
8975
8975
|
var require_ignore = __commonJS({
|
|
8976
|
-
"node_modules/
|
|
8976
|
+
"node_modules/ignore/index.js"(exports2, module2) {
|
|
8977
8977
|
function makeArray(subject) {
|
|
8978
8978
|
return Array.isArray(subject) ? subject : [subject];
|
|
8979
8979
|
}
|
|
@@ -241190,19 +241190,6 @@ function getNestedValue(obj, path4) {
|
|
|
241190
241190
|
}
|
|
241191
241191
|
return current;
|
|
241192
241192
|
}
|
|
241193
|
-
function setNestedValue(obj, path4, value) {
|
|
241194
|
-
const keys = path4.split(".");
|
|
241195
|
-
let current = obj;
|
|
241196
|
-
for (let i = 0; i < keys.length - 1; i++) {
|
|
241197
|
-
const key = keys[i];
|
|
241198
|
-
if (current[key] === void 0 || typeof current[key] !== "object") {
|
|
241199
|
-
current[key] = {};
|
|
241200
|
-
}
|
|
241201
|
-
current = current[key];
|
|
241202
|
-
}
|
|
241203
|
-
const lastKey = keys[keys.length - 1];
|
|
241204
|
-
current[lastKey] = value;
|
|
241205
|
-
}
|
|
241206
241193
|
function computeAggregates(events) {
|
|
241207
241194
|
const aggregates = {
|
|
241208
241195
|
// Event counts
|
|
@@ -241216,15 +241203,12 @@ function computeAggregates(events) {
|
|
|
241216
241203
|
"warnLogs.count": events.filter((e) => e.type === "log" && (e.severityNumber ?? 0) >= 13 && (e.severityNumber ?? 0) <= 16).length,
|
|
241217
241204
|
"debugLogs.count": events.filter((e) => e.type === "log" && (e.severityNumber ?? 0) >= 5 && (e.severityNumber ?? 0) <= 8).length
|
|
241218
241205
|
};
|
|
241219
|
-
|
|
241206
|
+
const summaryEventPatterns = [".complete", ".summary", ".error", ".started"];
|
|
241207
|
+
const summaryEvents = events.filter((e) => summaryEventPatterns.some((pattern) => e.name.endsWith(pattern)));
|
|
241208
|
+
for (const event of summaryEvents) {
|
|
241220
241209
|
if (event.attributes) {
|
|
241221
241210
|
for (const [key, value] of Object.entries(event.attributes)) {
|
|
241222
|
-
|
|
241223
|
-
setNestedValue(aggregates, key, value);
|
|
241224
|
-
if (key.includes(".")) {
|
|
241225
|
-
aggregates[key] = value;
|
|
241226
|
-
}
|
|
241227
|
-
}
|
|
241211
|
+
aggregates[key] = value;
|
|
241228
241212
|
}
|
|
241229
241213
|
}
|
|
241230
241214
|
}
|
|
@@ -241233,6 +241217,21 @@ function computeAggregates(events) {
|
|
|
241233
241217
|
|
|
241234
241218
|
// node_modules/@principal-ai/principal-view-core/dist/narrative/template-parser.js
|
|
241235
241219
|
var import_handlebars = __toESM(require_lib());
|
|
241220
|
+
import_handlebars.default.registerHelper("eq", (a, b) => a === b);
|
|
241221
|
+
import_handlebars.default.registerHelper("ne", (a, b) => a !== b);
|
|
241222
|
+
import_handlebars.default.registerHelper("lt", (a, b) => a < b);
|
|
241223
|
+
import_handlebars.default.registerHelper("gt", (a, b) => a > b);
|
|
241224
|
+
import_handlebars.default.registerHelper("lte", (a, b) => a <= b);
|
|
241225
|
+
import_handlebars.default.registerHelper("gte", (a, b) => a >= b);
|
|
241226
|
+
import_handlebars.default.registerHelper("and", (...args) => {
|
|
241227
|
+
const values = args.slice(0, -1);
|
|
241228
|
+
return values.every(Boolean);
|
|
241229
|
+
});
|
|
241230
|
+
import_handlebars.default.registerHelper("or", (...args) => {
|
|
241231
|
+
const values = args.slice(0, -1);
|
|
241232
|
+
return values.some(Boolean);
|
|
241233
|
+
});
|
|
241234
|
+
import_handlebars.default.registerHelper("not", (a) => !a);
|
|
241236
241235
|
function parseTemplate(template, context) {
|
|
241237
241236
|
try {
|
|
241238
241237
|
const handlebarTemplate = import_handlebars.default.compile(template, { noEscape: true });
|
|
@@ -241319,7 +241318,25 @@ function renderSpanTree(tree, scenario, context, formatting2) {
|
|
|
241319
241318
|
const parts = [];
|
|
241320
241319
|
for (const node of tree) {
|
|
241321
241320
|
const indent = (formatting2.indentPerLevel || " ").repeat(node.depth);
|
|
241322
|
-
const eventContext = { ...context
|
|
241321
|
+
const eventContext = { ...context };
|
|
241322
|
+
if (node.span.attributes) {
|
|
241323
|
+
for (const [key, value] of Object.entries(node.span.attributes)) {
|
|
241324
|
+
if (key.includes(".")) {
|
|
241325
|
+
const parts2 = key.split(".");
|
|
241326
|
+
let current = eventContext;
|
|
241327
|
+
for (let i = 0; i < parts2.length - 1; i++) {
|
|
241328
|
+
if (!current[parts2[i]] || typeof current[parts2[i]] !== "object") {
|
|
241329
|
+
current[parts2[i]] = {};
|
|
241330
|
+
}
|
|
241331
|
+
current = current[parts2[i]];
|
|
241332
|
+
}
|
|
241333
|
+
current[parts2[parts2.length - 1]] = value;
|
|
241334
|
+
} else {
|
|
241335
|
+
eventContext[key] = value;
|
|
241336
|
+
}
|
|
241337
|
+
}
|
|
241338
|
+
}
|
|
241339
|
+
eventContext.span = node.span;
|
|
241323
241340
|
if (scenario.template.span) {
|
|
241324
241341
|
const spanText = parseTemplate(scenario.template.span, eventContext);
|
|
241325
241342
|
parts.push(indent + spanText);
|
|
@@ -241353,7 +241370,24 @@ function renderTimeline(events, scenario, context, formatting2) {
|
|
|
241353
241370
|
return aTime - bTime;
|
|
241354
241371
|
});
|
|
241355
241372
|
for (const event of sorted) {
|
|
241356
|
-
const eventContext = { ...context
|
|
241373
|
+
const eventContext = { ...context };
|
|
241374
|
+
if (event.attributes) {
|
|
241375
|
+
for (const [key, value] of Object.entries(event.attributes)) {
|
|
241376
|
+
if (key.includes(".")) {
|
|
241377
|
+
const parts2 = key.split(".");
|
|
241378
|
+
let current = eventContext;
|
|
241379
|
+
for (let i = 0; i < parts2.length - 1; i++) {
|
|
241380
|
+
if (!current[parts2[i]] || typeof current[parts2[i]] !== "object") {
|
|
241381
|
+
current[parts2[i]] = {};
|
|
241382
|
+
}
|
|
241383
|
+
current = current[parts2[i]];
|
|
241384
|
+
}
|
|
241385
|
+
current[parts2[parts2.length - 1]] = value;
|
|
241386
|
+
} else {
|
|
241387
|
+
eventContext[key] = value;
|
|
241388
|
+
}
|
|
241389
|
+
}
|
|
241390
|
+
}
|
|
241357
241391
|
let eventText;
|
|
241358
241392
|
if (event.type === "log") {
|
|
241359
241393
|
eventText = renderLog(event, scenario, { ...eventContext, log: event }, formatting2);
|
|
@@ -241673,8 +241707,8 @@ var NarrativeValidator = class {
|
|
|
241673
241707
|
}
|
|
241674
241708
|
const canvasEvents = /* @__PURE__ */ new Set();
|
|
241675
241709
|
for (const node of canvas.nodes) {
|
|
241676
|
-
if (node.pv?.event
|
|
241677
|
-
canvasEvents.add(node.pv.event
|
|
241710
|
+
if (node.pv?.event && typeof node.pv.event === "string") {
|
|
241711
|
+
canvasEvents.add(node.pv.event);
|
|
241678
241712
|
}
|
|
241679
241713
|
}
|
|
241680
241714
|
const narrativeEvents = /* @__PURE__ */ new Set();
|
|
@@ -241688,7 +241722,9 @@ var NarrativeValidator = class {
|
|
|
241688
241722
|
}
|
|
241689
241723
|
if (scenario.template?.events) {
|
|
241690
241724
|
for (const eventName of Object.keys(scenario.template.events)) {
|
|
241691
|
-
|
|
241725
|
+
if (!eventName.includes("*")) {
|
|
241726
|
+
narrativeEvents.add(eventName);
|
|
241727
|
+
}
|
|
241692
241728
|
}
|
|
241693
241729
|
}
|
|
241694
241730
|
}
|
|
@@ -242104,7 +242140,7 @@ var NarrativeValidator = class {
|
|
|
242104
242140
|
path: path4,
|
|
242105
242141
|
message: `Incomplete conditional expression: ${expr}`,
|
|
242106
242142
|
impact: "Template will fail to render",
|
|
242107
|
-
suggestion:
|
|
242143
|
+
suggestion: "Use Handlebars syntax: {{#if condition}}true{{else}}false{{/if}}",
|
|
242108
242144
|
fixable: false
|
|
242109
242145
|
});
|
|
242110
242146
|
}
|
|
@@ -242112,12 +242148,137 @@ var NarrativeValidator = class {
|
|
|
242112
242148
|
return violations;
|
|
242113
242149
|
}
|
|
242114
242150
|
/**
|
|
242115
|
-
* Check attribute references
|
|
242151
|
+
* Check attribute references against execution data
|
|
242152
|
+
*
|
|
242153
|
+
* Validates that:
|
|
242154
|
+
* - Attributes referenced in templates exist in execution data
|
|
242155
|
+
* - Object attributes are accessed via properties (not used directly)
|
|
242156
|
+
* - Attribute names are correct (catches typos)
|
|
242116
242157
|
*/
|
|
242117
|
-
checkAttributeReferences(
|
|
242158
|
+
checkAttributeReferences(context) {
|
|
242118
242159
|
const violations = [];
|
|
242160
|
+
const { narrative, narrativePath, executionData } = context;
|
|
242161
|
+
if (!executionData) {
|
|
242162
|
+
return violations;
|
|
242163
|
+
}
|
|
242164
|
+
const { aggregates, eventAttributes } = executionData;
|
|
242165
|
+
for (const scenario of narrative.scenarios) {
|
|
242166
|
+
const scenarioPath = `scenarios[${scenario.id}]`;
|
|
242167
|
+
if (scenario.template.introduction) {
|
|
242168
|
+
const attrs = this.extractAttributeReferences(scenario.template.introduction);
|
|
242169
|
+
violations.push(...this.validateAttributes(
|
|
242170
|
+
attrs,
|
|
242171
|
+
aggregates,
|
|
242172
|
+
null,
|
|
242173
|
+
// introduction doesn't have specific event context
|
|
242174
|
+
narrativePath,
|
|
242175
|
+
`${scenarioPath}.template.introduction`
|
|
242176
|
+
));
|
|
242177
|
+
}
|
|
242178
|
+
if (scenario.template.events) {
|
|
242179
|
+
for (const [eventName, eventTemplate] of Object.entries(scenario.template.events)) {
|
|
242180
|
+
const attrs = this.extractAttributeReferences(eventTemplate);
|
|
242181
|
+
const eventAttrs = eventAttributes.get(eventName);
|
|
242182
|
+
violations.push(...this.validateAttributes(attrs, aggregates, eventAttrs || null, narrativePath, `${scenarioPath}.template.events.${eventName}`, eventName));
|
|
242183
|
+
}
|
|
242184
|
+
}
|
|
242185
|
+
if (scenario.template.summary) {
|
|
242186
|
+
const attrs = this.extractAttributeReferences(scenario.template.summary);
|
|
242187
|
+
violations.push(...this.validateAttributes(
|
|
242188
|
+
attrs,
|
|
242189
|
+
aggregates,
|
|
242190
|
+
null,
|
|
242191
|
+
// summary uses global aggregates
|
|
242192
|
+
narrativePath,
|
|
242193
|
+
`${scenarioPath}.template.summary`
|
|
242194
|
+
));
|
|
242195
|
+
}
|
|
242196
|
+
}
|
|
242119
242197
|
return violations;
|
|
242120
242198
|
}
|
|
242199
|
+
/**
|
|
242200
|
+
* Validate a list of attribute references against available data
|
|
242201
|
+
*
|
|
242202
|
+
* @param attributes - Attribute paths to validate
|
|
242203
|
+
* @param aggregates - Global aggregate attributes
|
|
242204
|
+
* @param eventAttributes - Event-specific attributes (if validating event template)
|
|
242205
|
+
* @param file - File path for violation reporting
|
|
242206
|
+
* @param path - JSON path for violation reporting
|
|
242207
|
+
* @param eventName - Event name (if validating event template)
|
|
242208
|
+
* @returns Array of violations found
|
|
242209
|
+
*/
|
|
242210
|
+
validateAttributes(attributes, aggregates, eventAttributes, file, path4, eventName) {
|
|
242211
|
+
const violations = [];
|
|
242212
|
+
for (const attr of attributes) {
|
|
242213
|
+
const globalValue = aggregates[attr];
|
|
242214
|
+
const eventValue = eventAttributes?.[attr];
|
|
242215
|
+
if (globalValue === void 0 && eventValue === void 0) {
|
|
242216
|
+
const allKeys = [
|
|
242217
|
+
...Object.keys(aggregates),
|
|
242218
|
+
...eventAttributes ? Object.keys(eventAttributes) : []
|
|
242219
|
+
];
|
|
242220
|
+
const similar = this.findSimilarAttributes(attr, allKeys);
|
|
242221
|
+
violations.push({
|
|
242222
|
+
ruleId: "narrative-attribute-undefined",
|
|
242223
|
+
severity: "warn",
|
|
242224
|
+
file,
|
|
242225
|
+
path: path4,
|
|
242226
|
+
message: eventName ? `Attribute "{{${attr}}}" not found in event "${eventName}" or global aggregates` : `Attribute "{{${attr}}}" not found in execution data`,
|
|
242227
|
+
impact: 'Template will render as empty or "undefined"',
|
|
242228
|
+
suggestion: similar.length > 0 ? `Did you mean: ${similar.join(", ")}?` : void 0,
|
|
242229
|
+
fixable: false
|
|
242230
|
+
});
|
|
242231
|
+
continue;
|
|
242232
|
+
}
|
|
242233
|
+
const value = eventValue !== void 0 ? eventValue : globalValue;
|
|
242234
|
+
if (this.isObjectType(value)) {
|
|
242235
|
+
const objectKeys = Object.keys(value);
|
|
242236
|
+
const suggestions = objectKeys.slice(0, 3).map((k) => `{{${attr}.${k}}}`);
|
|
242237
|
+
violations.push({
|
|
242238
|
+
ruleId: "narrative-attribute-object",
|
|
242239
|
+
severity: "warn",
|
|
242240
|
+
file,
|
|
242241
|
+
path: path4,
|
|
242242
|
+
message: `Attribute "{{${attr}}}" is an object and will render as "[object Object]"`,
|
|
242243
|
+
impact: 'Template will show "[object Object]" instead of useful data',
|
|
242244
|
+
suggestion: `Access a property instead: ${suggestions.join(", ")}`,
|
|
242245
|
+
fixable: false
|
|
242246
|
+
});
|
|
242247
|
+
}
|
|
242248
|
+
}
|
|
242249
|
+
return violations;
|
|
242250
|
+
}
|
|
242251
|
+
/**
|
|
242252
|
+
* Find similar attribute names for helpful suggestions
|
|
242253
|
+
*
|
|
242254
|
+
* Uses simple string similarity (Levenshtein-like) to find typos
|
|
242255
|
+
*
|
|
242256
|
+
* @param target - The attribute being searched for
|
|
242257
|
+
* @param available - Available attribute names
|
|
242258
|
+
* @returns Array of similar attribute names (max 3)
|
|
242259
|
+
*/
|
|
242260
|
+
findSimilarAttributes(target, available) {
|
|
242261
|
+
const similar = [];
|
|
242262
|
+
for (const attr of available) {
|
|
242263
|
+
if (attr.startsWith(target) || target.startsWith(attr)) {
|
|
242264
|
+
similar.push({ attr, score: 10 });
|
|
242265
|
+
continue;
|
|
242266
|
+
}
|
|
242267
|
+
if (attr.includes(target) || target.includes(attr)) {
|
|
242268
|
+
similar.push({ attr, score: 5 });
|
|
242269
|
+
continue;
|
|
242270
|
+
}
|
|
242271
|
+
const targetParts = target.split(".");
|
|
242272
|
+
const attrParts = attr.split(".");
|
|
242273
|
+
if (targetParts.length === attrParts.length) {
|
|
242274
|
+
const matchingParts = targetParts.filter((p, i) => p === attrParts[i]).length;
|
|
242275
|
+
if (matchingParts > 0) {
|
|
242276
|
+
similar.push({ attr, score: matchingParts });
|
|
242277
|
+
}
|
|
242278
|
+
}
|
|
242279
|
+
}
|
|
242280
|
+
return similar.sort((a, b) => b.score - a.score).slice(0, 3).map((s) => s.attr);
|
|
242281
|
+
}
|
|
242121
242282
|
/**
|
|
242122
242283
|
* Check formatting options
|
|
242123
242284
|
*/
|
|
@@ -242219,6 +242380,71 @@ var NarrativeValidator = class {
|
|
|
242219
242380
|
}
|
|
242220
242381
|
return match[1];
|
|
242221
242382
|
}
|
|
242383
|
+
/**
|
|
242384
|
+
* Extract attribute references from Handlebars template
|
|
242385
|
+
*
|
|
242386
|
+
* Parses template strings like:
|
|
242387
|
+
* - "{{source}}" -> ["source"]
|
|
242388
|
+
* - "{{source.url}}" -> ["source.url"]
|
|
242389
|
+
* - "{{#if options.global}}" -> ["options.global"]
|
|
242390
|
+
* - "{{#if (eq install.mode 'symlink')}}" -> ["install.mode"]
|
|
242391
|
+
*
|
|
242392
|
+
* @param template - Handlebars template string
|
|
242393
|
+
* @returns Array of attribute paths referenced in the template
|
|
242394
|
+
*/
|
|
242395
|
+
extractAttributeReferences(template) {
|
|
242396
|
+
const attributes = /* @__PURE__ */ new Set();
|
|
242397
|
+
const expressionPattern = /\{\{([^}]+)\}\}/g;
|
|
242398
|
+
let match;
|
|
242399
|
+
while ((match = expressionPattern.exec(template)) !== null) {
|
|
242400
|
+
const expression = match[1].trim();
|
|
242401
|
+
if (expression.startsWith("/")) {
|
|
242402
|
+
continue;
|
|
242403
|
+
}
|
|
242404
|
+
if (expression.startsWith("#")) {
|
|
242405
|
+
const helperMatch = expression.match(/^#\w+\s+(.+)$/);
|
|
242406
|
+
if (helperMatch) {
|
|
242407
|
+
this.extractAttributesFromExpression(helperMatch[1], attributes);
|
|
242408
|
+
}
|
|
242409
|
+
continue;
|
|
242410
|
+
}
|
|
242411
|
+
this.extractAttributesFromExpression(expression, attributes);
|
|
242412
|
+
}
|
|
242413
|
+
return Array.from(attributes);
|
|
242414
|
+
}
|
|
242415
|
+
/**
|
|
242416
|
+
* Extract attribute references from a single Handlebars expression
|
|
242417
|
+
*
|
|
242418
|
+
* Handles:
|
|
242419
|
+
* - Simple references: source.url
|
|
242420
|
+
* - Helper calls: (eq install.mode 'symlink')
|
|
242421
|
+
* - Nested expressions
|
|
242422
|
+
*
|
|
242423
|
+
* @param expression - The expression to parse
|
|
242424
|
+
* @param attributes - Set to add found attributes to
|
|
242425
|
+
*/
|
|
242426
|
+
extractAttributesFromExpression(expression, attributes) {
|
|
242427
|
+
const cleaned = expression.replace(/^\(|\)$/g, "").trim();
|
|
242428
|
+
const parts = cleaned.split(/\s+/);
|
|
242429
|
+
for (const part of parts) {
|
|
242430
|
+
if (part.match(/^(if|unless|each|with|eq|ne|lt|gt|lte|gte|and|or|not)$/) || part.match(/^['"].*['"]$/) || part.match(/^\d+$/) || part.match(/^(true|false|null|undefined)$/)) {
|
|
242431
|
+
continue;
|
|
242432
|
+
}
|
|
242433
|
+
const cleanPart = part.replace(/['"()]/g, "");
|
|
242434
|
+
if (cleanPart && cleanPart.match(/^[a-zA-Z_][a-zA-Z0-9_.]*$/)) {
|
|
242435
|
+
attributes.add(cleanPart);
|
|
242436
|
+
}
|
|
242437
|
+
}
|
|
242438
|
+
}
|
|
242439
|
+
/**
|
|
242440
|
+
* Check if an attribute is an object type
|
|
242441
|
+
*
|
|
242442
|
+
* @param value - The attribute value to check
|
|
242443
|
+
* @returns true if value is a plain object (not array, not null)
|
|
242444
|
+
*/
|
|
242445
|
+
isObjectType(value) {
|
|
242446
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
|
|
242447
|
+
}
|
|
242222
242448
|
};
|
|
242223
242449
|
function createNarrativeValidator() {
|
|
242224
242450
|
return new NarrativeValidator();
|
|
@@ -247413,13 +247639,18 @@ ${source_default.dim("\u2502")} ${source_default.yellow('"type"')}: "text"
|
|
|
247413
247639
|
${source_default.dim("\u2502")} ${source_default.yellow('"text"')}: "# Event Name", ${source_default.dim("// Markdown description")} ${source_default.dim("\u2502")}
|
|
247414
247640
|
${source_default.dim("\u2502")} ${source_default.yellow('"x"')}: 0, ${source_default.yellow('"y"')}: 0, ${source_default.yellow('"width"')}: 200, ${source_default.yellow('"height"')}: 100, ${source_default.dim("\u2502")}
|
|
247415
247641
|
${source_default.dim("\u2502")} ${source_default.green('"pv"')}: { ${source_default.dim("\u2502")}
|
|
247416
|
-
${source_default.dim("\u2502")} ${source_default.cyan('"
|
|
247417
|
-
${source_default.dim("\u2502")}
|
|
247418
|
-
${source_default.dim("\u2502")}
|
|
247419
|
-
${source_default.dim("\u2502")}
|
|
247420
|
-
${source_default.dim("\u2502")}
|
|
247421
|
-
${source_default.dim("\u2502")}
|
|
247422
|
-
${source_default.dim("\u2502")} }
|
|
247642
|
+
${source_default.dim("\u2502")} ${source_default.cyan('"event"')}: "feature.event.name", ${source_default.dim("// Event name")} ${source_default.dim("\u2502")}
|
|
247643
|
+
${source_default.dim("\u2502")} ${source_default.cyan('"sources"')}: ["src/file.ts"], ${source_default.dim("// Source files")} ${source_default.dim("\u2502")}
|
|
247644
|
+
${source_default.dim("\u2502")} ${source_default.cyan('"otel"')}: { ${source_default.dim("// OTEL metadata")} ${source_default.dim("\u2502")}
|
|
247645
|
+
${source_default.dim("\u2502")} ${source_default.yellow('"kind"')}: "event", ${source_default.dim("\u2502")}
|
|
247646
|
+
${source_default.dim("\u2502")} ${source_default.yellow('"category"')}: "lifecycle" ${source_default.dim("\u2502")}
|
|
247647
|
+
${source_default.dim("\u2502")} }, ${source_default.dim("\u2502")}
|
|
247648
|
+
${source_default.dim("\u2502")} ${source_default.cyan('"dataSchema"')}: { ${source_default.dim("// Attribute definitions")} ${source_default.dim("\u2502")}
|
|
247649
|
+
${source_default.dim("\u2502")} ${source_default.yellow('"attr.name"')}: { ${source_default.dim("\u2502")}
|
|
247650
|
+
${source_default.dim("\u2502")} ${source_default.green('"type"')}: "string", ${source_default.dim("\u2502")}
|
|
247651
|
+
${source_default.dim("\u2502")} ${source_default.green('"required"')}: true ${source_default.dim("\u2502")}
|
|
247652
|
+
${source_default.dim("\u2502")} } ${source_default.dim("\u2502")}
|
|
247653
|
+
${source_default.dim("\u2502")} } ${source_default.dim("\u2502")}
|
|
247423
247654
|
${source_default.dim("\u2502")} } ${source_default.dim("\u2502")}
|
|
247424
247655
|
${source_default.dim("\u2502")} } ${source_default.dim("\u2502")}
|
|
247425
247656
|
${source_default.dim("\u2502")} ], ${source_default.dim("\u2502")}
|
|
@@ -247526,12 +247757,26 @@ ${source_default.cyan("2. Standard scenario set:")}
|
|
|
247526
247757
|
- ${source_default.yellow("Failure")} (priority 2): Feature encountered error
|
|
247527
247758
|
- ${source_default.dim("Fallback")} (priority 999): Generic execution captured
|
|
247528
247759
|
|
|
247529
|
-
${source_default.cyan("3. Template
|
|
247530
|
-
|
|
247531
|
-
|
|
247532
|
-
|
|
247533
|
-
- {{
|
|
247534
|
-
- {{result.
|
|
247760
|
+
${source_default.cyan("3. Template syntax (Handlebars):")}
|
|
247761
|
+
Templates use Handlebars syntax for dynamic content:
|
|
247762
|
+
|
|
247763
|
+
${source_default.bold("Variables:")}
|
|
247764
|
+
- {{variable}} Simple variable
|
|
247765
|
+
- {{result.count}} Nested property
|
|
247766
|
+
- {{error.message}} Deeply nested
|
|
247767
|
+
|
|
247768
|
+
${source_default.bold("Conditionals:")}
|
|
247769
|
+
- {{#if condition}}...{{/if}} If block
|
|
247770
|
+
- {{#if condition}}...{{else}}...{{/if}} If-else
|
|
247771
|
+
- {{#if (eq status "ok")}}\u2705{{else}}\u274C{{/if}} Comparison
|
|
247772
|
+
|
|
247773
|
+
${source_default.bold("Loops:")}
|
|
247774
|
+
- {{#each items}}{{this}}{{/each}} Iterate array
|
|
247775
|
+
- {{#each items}}{{@index}}: {{this}}{{/each}} With index
|
|
247776
|
+
|
|
247777
|
+
${source_default.bold("Comparison helpers:")}
|
|
247778
|
+
- eq, ne, lt, gt, lte, gte, and, or, not
|
|
247779
|
+
- Example: {{#if (gt count 10)}}Many{{/if}}
|
|
247535
247780
|
|
|
247536
247781
|
${source_default.cyan("4. Template style:")}
|
|
247537
247782
|
- Clear, concise summary line
|
|
@@ -247657,11 +247902,20 @@ ${source_default.yellow(".principal-views/data-validator.otel.canvas")}
|
|
|
247657
247902
|
"text": "# validation.started\\n\\nEmitted when validation begins",
|
|
247658
247903
|
"x": 0, "y": 0, "width": 200, "height": 100,
|
|
247659
247904
|
"pv": {
|
|
247660
|
-
"
|
|
247661
|
-
|
|
247662
|
-
|
|
247663
|
-
|
|
247664
|
-
|
|
247905
|
+
"event": "validation.started",
|
|
247906
|
+
"sources": ["src/validator.ts"],
|
|
247907
|
+
"otel": {
|
|
247908
|
+
"kind": "event",
|
|
247909
|
+
"category": "lifecycle"
|
|
247910
|
+
},
|
|
247911
|
+
"dataSchema": {
|
|
247912
|
+
"input.recordCount": {
|
|
247913
|
+
"type": "number",
|
|
247914
|
+
"required": true
|
|
247915
|
+
},
|
|
247916
|
+
"input.source": {
|
|
247917
|
+
"type": "string",
|
|
247918
|
+
"required": false
|
|
247665
247919
|
}
|
|
247666
247920
|
}
|
|
247667
247921
|
}
|
|
@@ -247672,11 +247926,24 @@ ${source_default.yellow(".principal-views/data-validator.otel.canvas")}
|
|
|
247672
247926
|
"text": "# validation.complete\\n\\nEmitted when validation succeeds",
|
|
247673
247927
|
"x": 250, "y": 0, "width": 200, "height": 100,
|
|
247674
247928
|
"pv": {
|
|
247675
|
-
"
|
|
247676
|
-
|
|
247677
|
-
|
|
247678
|
-
|
|
247679
|
-
|
|
247929
|
+
"event": "validation.complete",
|
|
247930
|
+
"sources": ["src/validator.ts"],
|
|
247931
|
+
"otel": {
|
|
247932
|
+
"kind": "event",
|
|
247933
|
+
"category": "lifecycle"
|
|
247934
|
+
},
|
|
247935
|
+
"dataSchema": {
|
|
247936
|
+
"result.validCount": {
|
|
247937
|
+
"type": "number",
|
|
247938
|
+
"required": true
|
|
247939
|
+
},
|
|
247940
|
+
"result.invalidCount": {
|
|
247941
|
+
"type": "number",
|
|
247942
|
+
"required": true
|
|
247943
|
+
},
|
|
247944
|
+
"duration.ms": {
|
|
247945
|
+
"type": "number",
|
|
247946
|
+
"required": false
|
|
247680
247947
|
}
|
|
247681
247948
|
}
|
|
247682
247949
|
}
|
|
@@ -247687,11 +247954,24 @@ ${source_default.yellow(".principal-views/data-validator.otel.canvas")}
|
|
|
247687
247954
|
"text": "# validation.error\\n\\nEmitted when validation fails",
|
|
247688
247955
|
"x": 500, "y": 0, "width": 200, "height": 100,
|
|
247689
247956
|
"pv": {
|
|
247690
|
-
"
|
|
247691
|
-
|
|
247692
|
-
|
|
247693
|
-
|
|
247694
|
-
|
|
247957
|
+
"event": "validation.error",
|
|
247958
|
+
"sources": ["src/validator.ts"],
|
|
247959
|
+
"otel": {
|
|
247960
|
+
"kind": "event",
|
|
247961
|
+
"category": "error"
|
|
247962
|
+
},
|
|
247963
|
+
"dataSchema": {
|
|
247964
|
+
"error.type": {
|
|
247965
|
+
"type": "string",
|
|
247966
|
+
"required": true
|
|
247967
|
+
},
|
|
247968
|
+
"error.message": {
|
|
247969
|
+
"type": "string",
|
|
247970
|
+
"required": true
|
|
247971
|
+
},
|
|
247972
|
+
"error.stage": {
|
|
247973
|
+
"type": "string",
|
|
247974
|
+
"required": false
|
|
247695
247975
|
}
|
|
247696
247976
|
}
|
|
247697
247977
|
}
|
|
@@ -249024,7 +249304,7 @@ function capitalize(str3) {
|
|
|
249024
249304
|
// src/commands/narrative/validate.ts
|
|
249025
249305
|
function createValidateCommand2() {
|
|
249026
249306
|
const command = new Command("validate");
|
|
249027
|
-
command.description("Validate narrative template syntax, schema, and references").argument("<file>", "Path to .narrative.json file").option("--canvas <path>", "Override canvas file path for validation").option("--json", "Output violations as JSON").option("-q, --quiet", "Only show errors, suppress warnings").option("-d, --dir <path>", "Project directory (default: cwd)").action(async (file, options) => {
|
|
249307
|
+
command.description("Validate narrative template syntax, schema, and references").argument("<file>", "Path to .narrative.json file").option("--canvas <path>", "Override canvas file path for validation").option("--execution <path>", "Execution file (.otel.json) for validating attribute references").option("--json", "Output violations as JSON").option("-q, --quiet", "Only show errors, suppress warnings").option("-d, --dir <path>", "Project directory (default: cwd)").action(async (file, options) => {
|
|
249028
249308
|
try {
|
|
249029
249309
|
const baseDir = options.dir || process.cwd();
|
|
249030
249310
|
const narrativePath = resolvePath(file, baseDir);
|
|
@@ -249045,13 +249325,40 @@ function createValidateCommand2() {
|
|
|
249045
249325
|
canvas = void 0;
|
|
249046
249326
|
}
|
|
249047
249327
|
}
|
|
249328
|
+
let executionData;
|
|
249329
|
+
if (options.execution) {
|
|
249330
|
+
try {
|
|
249331
|
+
const executionPath = resolvePath(options.execution, baseDir);
|
|
249332
|
+
const execution = await loadExecution(executionPath);
|
|
249333
|
+
const events = executionToEvents(execution);
|
|
249334
|
+
const aggregates = computeAggregates(events);
|
|
249335
|
+
const eventAttributes = /* @__PURE__ */ new Map();
|
|
249336
|
+
for (const event of events) {
|
|
249337
|
+
if (!eventAttributes.has(event.name)) {
|
|
249338
|
+
eventAttributes.set(event.name, {});
|
|
249339
|
+
}
|
|
249340
|
+
const attrs = eventAttributes.get(event.name);
|
|
249341
|
+
if (event.attributes) {
|
|
249342
|
+
Object.assign(attrs, event.attributes);
|
|
249343
|
+
}
|
|
249344
|
+
}
|
|
249345
|
+
executionData = { aggregates, eventAttributes };
|
|
249346
|
+
} catch (error) {
|
|
249347
|
+
console.error(
|
|
249348
|
+
source_default.yellow("Warning:"),
|
|
249349
|
+
`Failed to load execution file: ${error.message}`
|
|
249350
|
+
);
|
|
249351
|
+
console.error(source_default.gray(" Attribute validation will be skipped"));
|
|
249352
|
+
}
|
|
249353
|
+
}
|
|
249048
249354
|
const validator = new NarrativeValidator();
|
|
249049
249355
|
const context = {
|
|
249050
249356
|
narrative,
|
|
249051
249357
|
narrativePath,
|
|
249052
249358
|
canvasPath,
|
|
249053
249359
|
canvas,
|
|
249054
|
-
basePath: baseDir
|
|
249360
|
+
basePath: baseDir,
|
|
249361
|
+
executionData
|
|
249055
249362
|
};
|
|
249056
249363
|
const result = await validator.validate(context);
|
|
249057
249364
|
const violations = options.quiet ? result.violations.filter((v) => v.severity === "error") : result.violations;
|
|
@@ -249075,7 +249382,8 @@ function createValidateCommand2() {
|
|
|
249075
249382
|
errors: errors.length,
|
|
249076
249383
|
warnings: warnings.length,
|
|
249077
249384
|
scenarioCount: narrative.scenarios.length,
|
|
249078
|
-
hasDefault: narrative.scenarios.some((s) => s.condition.default)
|
|
249385
|
+
hasDefault: narrative.scenarios.some((s) => s.condition.default),
|
|
249386
|
+
attributeValidation: executionData ? "enabled" : "skipped"
|
|
249079
249387
|
}
|
|
249080
249388
|
};
|
|
249081
249389
|
console.log(JSON.stringify(output, null, 2));
|
|
@@ -249140,6 +249448,17 @@ ${icon} ${severity}: ${violation.message}`);
|
|
|
249140
249448
|
if (canvasPath) {
|
|
249141
249449
|
console.log(source_default.gray(` \u2022 Canvas: ${narrative.canvas || canvasPath}`));
|
|
249142
249450
|
}
|
|
249451
|
+
if (executionData) {
|
|
249452
|
+
console.log(
|
|
249453
|
+
source_default.gray(" \u2022 Attribute validation:"),
|
|
249454
|
+
source_default.green("enabled")
|
|
249455
|
+
);
|
|
249456
|
+
} else {
|
|
249457
|
+
console.log(
|
|
249458
|
+
source_default.gray(" \u2022 Attribute validation:"),
|
|
249459
|
+
source_default.gray("skipped (use --execution to enable)")
|
|
249460
|
+
);
|
|
249461
|
+
}
|
|
249143
249462
|
console.log();
|
|
249144
249463
|
}
|
|
249145
249464
|
if (errors.length > 0) {
|