Skip to main content

Query Package

The @traceweave/trf-query package provides SQL query capabilities for .twpack files, allowing flexible data extraction and analysis.

Installation

npm install @traceweave/trf-query

Quick Start

const { TwpackQuery } = require('@traceweave/trf-query');

const query = new TwpackQuery();
await query.loadTwpack('./my-pack.twpack');

// Get all requirements
const requirements = query.getByKind('requirement');

// Execute SQL query
const results = query.sql(
'SELECT kind, COUNT(*) as count FROM artifacts GROUP BY kind'
);

// Always close when done
query.close();

API Reference

TwpackQuery

loadTwpack(path)

Load a .twpack file for querying.

await query.loadTwpack('./my-pack.twpack');

sql(query, params?)

Execute a raw SQL query.

// Simple query
const results = query.sql('SELECT * FROM artifacts WHERE kind = ?', ['requirement']);

// Complex query
const results = query.sql(`
SELECT
a.id,
a.title,
l.relation,
b.title as linked_to
FROM artifacts a
JOIN links l ON a.id = l.from_id
JOIN artifacts b ON l.to_id = b.id
WHERE a.kind = 'requirement'
`);

Returns an array of result objects.

getByKind(kind)

Get all artifacts of a specific kind.

const requirements = query.getByKind('requirement');
const tests = query.getByKind('test');

getByRelation(relation)

Get all links with a specific relation.

const verifiedBy = query.getByRelation('verified_by');
const implements = query.getByRelation('implements');

getArtifacts(filter?)

Get artifacts with optional filtering.

// All artifacts
const all = query.getArtifacts();

// With predicate filter
const filtered = query.getArtifacts({
predicate: (artifact) => artifact.kind === 'requirement' &&
artifact.fields.priority === 'high'
});

getLinks(filter?)

Get links with optional filtering.

// All links
const all = query.getLinks();

// With predicate filter
const filtered = query.getLinks({
predicate: (link) => link.relation === 'verified_by'
});

close()

Close the query engine and cleanup resources.

query.close();

Important: Always call close() when done to release database connections.

Database Schema

The query engine loads .twpack data into SQLite tables:

artifacts table

ColumnTypeDescription
idTEXTArtifact ID (primary key)
kindTEXTArtifact kind
versionTEXTArtifact version
titleTEXTArtifact title
fieldsTEXTJSON string of custom fields
ColumnTypeDescription
idTEXTLink ID (primary key)
from_idTEXTSource artifact ID
to_idTEXTTarget artifact ID
relationTEXTRelation type

provenance table

ColumnTypeDescription
idTEXTActivity ID (primary key)
typeTEXTActivity type
timestampTEXTISO 8601 timestamp
actorTEXTActor/agent
fieldsTEXTJSON string of custom fields

Complete Examples

Basic Queries

const { TwpackQuery } = require('@traceweave/trf-query');

async function basicQueries() {
const query = new TwpackQuery();

try {
await query.loadTwpack('./my-pack.twpack');

// Count artifacts by kind
const kindCounts = query.sql(`
SELECT kind, COUNT(*) as count
FROM artifacts
GROUP BY kind
`);
console.log('Artifacts by kind:', kindCounts);

// Count links by relation
const relationCounts = query.sql(`
SELECT relation, COUNT(*) as count
FROM links
GROUP BY relation
`);
console.log('Links by relation:', relationCounts);

// Get all high-priority requirements
const highPriority = query.sql(`
SELECT id, title
FROM artifacts
WHERE kind = 'requirement'
AND json_extract(fields, '$.priority') = 'high'
`);
console.log('High priority requirements:', highPriority);

} finally {
query.close();
}
}

Advanced Joins

async function advancedQueries() {
const query = new TwpackQuery();

try {
await query.loadTwpack('./automotive-iso26262.twpack');

// Requirements with their tests
const reqsWithTests = query.sql(`
SELECT
r.id as requirement_id,
r.title as requirement_title,
t.id as test_id,
t.title as test_title,
json_extract(t.fields, '$.status') as test_status
FROM artifacts r
JOIN links l ON r.id = l.from_id
JOIN artifacts t ON l.to_id = t.id
WHERE r.kind = 'requirement'
AND t.kind = 'test'
AND l.relation = 'verified_by'
`);

console.log('Requirements with tests:');
reqsWithTests.forEach(row => {
console.log(` ${row.requirement_id}: ${row.requirement_title}`);
console.log(`${row.test_id} (${row.test_status})`);
});

// ASIL-D requirements without tests
const uncoveredASILD = query.sql(`
SELECT r.id, r.title
FROM artifacts r
LEFT JOIN links l ON r.id = l.from_id AND l.relation = 'verified_by'
WHERE r.kind = 'requirement'
AND json_extract(r.fields, '$.safety_level') = 'ASIL-D'
AND l.id IS NULL
`);

if (uncoveredASILD.length > 0) {
console.log('\n⚠️ ASIL-D requirements without tests:');
uncoveredASILD.forEach(req => {
console.log(` - ${req.id}: ${req.title}`);
});
}

} finally {
query.close();
}
}

JSON Field Queries

async function jsonFieldQueries() {
const query = new TwpackQuery();

try {
await query.loadTwpack('./my-pack.twpack');

// Query JSON fields using json_extract
const results = query.sql(`
SELECT
id,
title,
json_extract(fields, '$.priority') as priority,
json_extract(fields, '$.safety_level') as safety_level,
json_extract(fields, '$.status') as status
FROM artifacts
WHERE kind = 'requirement'
AND json_extract(fields, '$.priority') IN ('high', 'critical')
ORDER BY
CASE json_extract(fields, '$.priority')
WHEN 'critical' THEN 1
WHEN 'high' THEN 2
ELSE 3
END
`);

console.log('Priority requirements:');
results.forEach(row => {
console.log(` ${row.id} [${row.priority}]: ${row.title}`);
});

} finally {
query.close();
}
}

Traceability Matrix

async function traceabilityMatrix() {
const query = new TwpackQuery();

try {
await query.loadTwpack('./compliance.twpack');

// Create traceability matrix
const matrix = query.sql(`
SELECT
r.id as requirement,
GROUP_CONCAT(t.id, ', ') as tests
FROM artifacts r
LEFT JOIN links l ON r.id = l.from_id AND l.relation = 'verified_by'
LEFT JOIN artifacts t ON l.to_id = t.id AND t.kind = 'test'
WHERE r.kind = 'requirement'
GROUP BY r.id
ORDER BY r.id
`);

console.log('Traceability Matrix:');
console.log('Requirement | Tests');
console.log('------------|------');
matrix.forEach(row => {
console.log(`${row.requirement} | ${row.tests || '(none)'}`);
});

} finally {
query.close();
}
}

Export to JSON

async function exportData() {
const query = new TwpackQuery();

try {
await query.loadTwpack('./my-pack.twpack');

// Export all requirements with their metadata
const requirements = query.sql(`
SELECT
id,
title,
version,
fields
FROM artifacts
WHERE kind = 'requirement'
`);

// Parse JSON fields
const parsed = requirements.map(req => ({
id: req.id,
title: req.title,
version: req.version,
...JSON.parse(req.fields)
}));

// Write to file
const fs = require('fs');
fs.writeFileSync(
'./requirements-export.json',
JSON.stringify(parsed, null, 2)
);

console.log(`Exported ${parsed.length} requirements`);

} finally {
query.close();
}
}

CLI Usage

All query functionality is available via CLI:

# Get artifacts by kind
trf query artifacts -f my-pack.twpack -k requirement

# Get links by relation
trf query links -f my-pack.twpack -r verified_by

# Execute SQL query
trf query sql -f my-pack.twpack \
-q "SELECT kind, COUNT(*) FROM artifacts GROUP BY kind"

# JSON output for automation
trf query artifacts -f my-pack.twpack -k test --json > tests.json

See CLI Reference for details.

Performance

Query performance depends on the complexity and data size:

Query Type10K artifacts100K artifacts
Simple SELECT<5ms<20ms
Single JOIN<20ms<100ms
Complex JOIN<50ms<300ms
Aggregation<10ms<50ms

Tips

Indexing

The query engine automatically creates indexes on:

  • artifacts.id
  • artifacts.kind
  • links.from_id
  • links.to_id
  • links.relation

JSON Queries

Use json_extract() to query JSON fields:

-- Extract a field
json_extract(fields, '$.priority')

-- Nested extraction
json_extract(fields, '$.metadata.author')

-- Array access
json_extract(fields, '$.tags[0]')

Parameterized Queries

Always use parameterized queries to prevent SQL injection:

// Good
query.sql('SELECT * FROM artifacts WHERE kind = ?', [userInput]);

// Bad (vulnerable to injection)
query.sql(`SELECT * FROM artifacts WHERE kind = '${userInput}'`);

See Also