focused-spec 0.1.0

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/parser.js ADDED
@@ -0,0 +1,64 @@
1
+ const ID = /^- \*\*ID\*\*: `([^`]+)`$/u;
2
+ const EVIDENCE = /^- \*\*EVIDENCE\*\*: `([^`]+)`$/u;
3
+ const WHEN = /^- \*\*WHEN\*\*/u;
4
+ const THEN = /^- \*\*THEN\*\*/u;
5
+ const REQUIREMENT = /^### Requirement: (.+)$/u;
6
+ const SCENARIO = /^#### Scenario: (.+)$/u;
7
+ const MALFORMED_SCENARIO = /^(?:#{1,3}|#{5,}) Scenario:/u;
8
+ const OPERATION = /^## (ADDED|MODIFIED|REMOVED|RENAMED) Requirements$/u;
9
+ export const STABLE_ID = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/u;
10
+ export function parseFocusedSpecDocument(path, source) {
11
+ const lines = source.split('\n');
12
+ const scenarios = [];
13
+ const malformedScenarioHeadings = [];
14
+ let operation = 'CURRENT';
15
+ let requirement;
16
+ for (let index = 0; index < lines.length; index += 1) {
17
+ const line = lines[index] ?? '';
18
+ const operationMatch = OPERATION.exec(line);
19
+ if (operationMatch?.[1] !== undefined)
20
+ operation = operationMatch[1];
21
+ const requirementMatch = REQUIREMENT.exec(line);
22
+ if (requirementMatch?.[1] !== undefined)
23
+ requirement = requirementMatch[1];
24
+ if (MALFORMED_SCENARIO.test(line))
25
+ malformedScenarioHeadings.push(index + 1);
26
+ const scenarioMatch = SCENARIO.exec(line);
27
+ if (scenarioMatch?.[1] === undefined)
28
+ continue;
29
+ const body = [];
30
+ for (let cursor = index + 1; cursor < lines.length; cursor += 1) {
31
+ const candidate = lines[cursor] ?? '';
32
+ if (SCENARIO.test(candidate) || REQUIREMENT.test(candidate) || candidate.startsWith('## '))
33
+ break;
34
+ body.push(candidate);
35
+ }
36
+ scenarios.push({
37
+ path,
38
+ line: index + 1,
39
+ name: scenarioMatch[1],
40
+ ...(requirement === undefined ? {} : { requirement }),
41
+ operation,
42
+ ids: body.flatMap(value => ID.exec(value)?.[1] ?? []),
43
+ evidence: body.flatMap(value => EVIDENCE.exec(value)?.[1] ?? []),
44
+ whenCount: body.filter(value => WHEN.test(value)).length,
45
+ thenCount: body.filter(value => THEN.test(value)).length,
46
+ });
47
+ }
48
+ return { path, scenarios, malformedScenarioHeadings };
49
+ }
50
+ export function parseEvidenceReference(raw) {
51
+ const planned = raw.startsWith('planned:');
52
+ const value = planned ? raw.slice('planned:'.length) : raw;
53
+ const separator = value.indexOf('::');
54
+ if (separator <= 0 || separator === value.length - 2) {
55
+ return { error: 'expected [planned:]<runner-id>::<selector>' };
56
+ }
57
+ return {
58
+ raw,
59
+ planned,
60
+ runnerId: value.slice(0, separator),
61
+ selector: value.slice(separator + 2),
62
+ };
63
+ }
64
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ExecutionPlan, FocusedSpecConfig, SpecDocument, Violation } from './model.js';
2
+ export interface PlanOptions {
3
+ readonly scenarioId?: string;
4
+ }
5
+ export interface PlanOutput {
6
+ readonly plan?: ExecutionPlan;
7
+ readonly violations: readonly Violation[];
8
+ }
9
+ export declare function planEvidence(projectRoot: string, config: FocusedSpecConfig, documents: readonly SpecDocument[], options?: PlanOptions): Promise<PlanOutput>;
@@ -0,0 +1,100 @@
1
+ import { parseEvidenceReference } from './parser.js';
2
+ import { resolveRunnerTargets } from './runner-client.js';
3
+ export async function planEvidence(projectRoot, config, documents, options = {}) {
4
+ const uses = [];
5
+ const scenarioUses = new Map();
6
+ for (const document of documents) {
7
+ for (const scenario of document.scenarios) {
8
+ const scenarioId = scenario.ids[0];
9
+ if (scenarioId === undefined || (options.scenarioId !== undefined && scenarioId !== options.scenarioId))
10
+ continue;
11
+ const selected = [];
12
+ for (const raw of scenario.evidence) {
13
+ const reference = parseEvidenceReference(raw);
14
+ if ('error' in reference || reference.planned)
15
+ continue;
16
+ const use = { raw, runnerId: reference.runnerId, selector: reference.selector, path: scenario.path, line: scenario.line, scenarioId };
17
+ uses.push(use);
18
+ selected.push(use);
19
+ }
20
+ if (selected.length > 0)
21
+ scenarioUses.set(scenarioId, selected);
22
+ }
23
+ }
24
+ if (options.scenarioId !== undefined && !scenarioUses.has(options.scenarioId)) {
25
+ return { violations: [{ path: options.scenarioId, message: `selected scenario has no executable evidence: ${options.scenarioId}` }] };
26
+ }
27
+ const byRunner = new Map();
28
+ for (const use of uses)
29
+ byRunner.set(use.runnerId, [...(byRunner.get(use.runnerId) ?? []), use]);
30
+ const violations = [];
31
+ const targetsByRunnerSelector = new Map();
32
+ for (const [runnerId, runnerUses] of [...byRunner].sort(([left], [right]) => left.localeCompare(right))) {
33
+ const runnerConfig = config.runners[runnerId];
34
+ if (runnerConfig === undefined)
35
+ continue;
36
+ const selectors = [...new Set(runnerUses.map(use => use.selector))].sort();
37
+ try {
38
+ const resolved = await resolveRunnerTargets(projectRoot, runnerId, runnerConfig, selectors);
39
+ for (const error of resolved.errors) {
40
+ for (const use of runnerUses.filter(candidate => candidate.selector === error.selector)) {
41
+ violations.push({ path: use.path, line: use.line, scenarioId: use.scenarioId, message: `unresolved evidence ${use.raw}: ${error.message}` });
42
+ }
43
+ }
44
+ for (const target of resolved.targets) {
45
+ const references = [...new Set(runnerUses.filter(use => use.selector === target.selector).map(use => use.raw))];
46
+ const key = `${runnerId}\u0000${target.targetId}`;
47
+ const prior = targetsByRunnerSelector.get(`${runnerId}\u0000${target.selector}`);
48
+ if (prior !== undefined && prior.key !== key) {
49
+ violations.push({ path: runnerId, message: `runner ${runnerId} resolved selector ${target.selector} inconsistently` });
50
+ continue;
51
+ }
52
+ targetsByRunnerSelector.set(`${runnerId}\u0000${target.selector}`, {
53
+ key,
54
+ runnerId,
55
+ config: runnerConfig,
56
+ target,
57
+ references,
58
+ });
59
+ }
60
+ }
61
+ catch (error) {
62
+ violations.push({ path: runnerId, message: error instanceof Error ? error.message : String(error) });
63
+ }
64
+ }
65
+ if (violations.length > 0)
66
+ return { violations };
67
+ const targetsByKey = new Map();
68
+ for (const target of targetsByRunnerSelector.values()) {
69
+ const existing = targetsByKey.get(target.key);
70
+ if (existing === undefined)
71
+ targetsByKey.set(target.key, target);
72
+ else
73
+ targetsByKey.set(target.key, {
74
+ ...existing,
75
+ references: [...new Set([...existing.references, ...target.references])].sort(),
76
+ });
77
+ }
78
+ const scenarios = [];
79
+ for (const [id, selectedUses] of scenarioUses) {
80
+ const evidence = selectedUses.map(use => {
81
+ const target = targetsByRunnerSelector.get(`${use.runnerId}\u0000${use.selector}`);
82
+ if (target === undefined)
83
+ throw new Error(`planner lost resolved evidence ${use.raw}`);
84
+ return { key: target.key, reference: use.raw };
85
+ });
86
+ scenarios.push({ id, evidence });
87
+ }
88
+ scenarios.sort((left, right) => left.id.localeCompare(right.id));
89
+ const targets = [...targetsByKey.values()].sort((left, right) => left.key.localeCompare(right.key));
90
+ const grouped = new Map();
91
+ for (const target of targets)
92
+ grouped.set(target.runnerId, [...(grouped.get(target.runnerId) ?? []), target]);
93
+ const groups = [...grouped].sort(([left], [right]) => left.localeCompare(right)).map(([runnerId, runnerTargets]) => ({
94
+ runnerId,
95
+ config: config.runners[runnerId],
96
+ targets: runnerTargets,
97
+ }));
98
+ return { violations: [], plan: { projectRoot, scenarios, targets, groups } };
99
+ }
100
+ //# sourceMappingURL=planner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planner.js","sourceRoot":"","sources":["../src/planner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ export type JsonPrimitive = string | number | boolean | null;
2
+ export type JsonValue = JsonPrimitive | {
3
+ readonly [key: string]: JsonValue;
4
+ } | readonly JsonValue[];
5
+ export interface RunnerContext {
6
+ readonly projectRoot: string;
7
+ readonly cwd: string;
8
+ readonly runnerId: string;
9
+ readonly options: JsonValue;
10
+ readonly signal: AbortSignal;
11
+ }
12
+ export interface ResolveRequest extends RunnerContext {
13
+ readonly selectors: readonly string[];
14
+ }
15
+ export interface SourceLocation {
16
+ readonly path: string;
17
+ readonly line?: number;
18
+ }
19
+ export interface ResolvedTarget {
20
+ readonly selector: string;
21
+ readonly targetId: string;
22
+ readonly displayName: string;
23
+ readonly source?: SourceLocation;
24
+ readonly data?: JsonValue;
25
+ }
26
+ export interface ResolveError {
27
+ readonly selector: string;
28
+ readonly message: string;
29
+ }
30
+ export interface ResolveResponse {
31
+ readonly targets: readonly ResolvedTarget[];
32
+ readonly errors: readonly ResolveError[];
33
+ }
34
+ export interface RunRequest extends RunnerContext {
35
+ readonly targets: readonly ResolvedTarget[];
36
+ }
37
+ export type TargetStatus = 'pass' | 'fail' | 'skip';
38
+ export interface TargetResult {
39
+ readonly targetId: string;
40
+ readonly status: TargetStatus;
41
+ readonly diagnostic?: string;
42
+ }
43
+ export interface RunResponse {
44
+ readonly results: readonly TargetResult[];
45
+ }
46
+ export interface RunnerPlugin {
47
+ readonly apiVersion: 1;
48
+ resolve(request: ResolveRequest): Promise<ResolveResponse>;
49
+ run(request: RunRequest): Promise<RunResponse>;
50
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=runner-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner-api.js","sourceRoot":"","sources":["../src/runner-api.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ import type { RunnerConfig } from './model.js';
2
+ import type { ResolveResponse, ResolvedTarget, RunResponse } from './runner-api.js';
3
+ export declare function resolveRunnerTargets(projectRoot: string, runnerId: string, config: RunnerConfig, selectors: readonly string[]): Promise<ResolveResponse>;
4
+ export declare function runRunnerTargets(projectRoot: string, runnerId: string, config: RunnerConfig, targets: readonly ResolvedTarget[]): Promise<RunResponse>;
@@ -0,0 +1,193 @@
1
+ import { fork } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { access } from 'node:fs/promises';
4
+ import { extname, isAbsolute, relative, resolve, sep } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { isJsonValue } from './config.js';
7
+ const DEFAULT_TIMEOUT_MS = 120_000;
8
+ const MODULE_EXTENSIONS = new Set(['.js', '.mjs', '.ts', '.mts']);
9
+ const OUTPUT_LIMIT = 16_384;
10
+ function inside(root, path) {
11
+ const value = relative(resolve(root), resolve(path));
12
+ return value === '' || (!value.startsWith(`..${sep}`) && value !== '..' && !isAbsolute(value));
13
+ }
14
+ function text(value) {
15
+ return typeof value === 'string' && value.length > 0;
16
+ }
17
+ function boundedAppend(current, chunk) {
18
+ const next = current + String(chunk);
19
+ return next.length <= OUTPUT_LIMIT ? next : next.slice(next.length - OUTPUT_LIMIT);
20
+ }
21
+ async function runnerPaths(projectRoot, config) {
22
+ if (isAbsolute(config.module))
23
+ throw new Error('runner module must be project-relative');
24
+ const modulePath = resolve(projectRoot, config.module);
25
+ if (!inside(projectRoot, modulePath))
26
+ throw new Error('runner module must stay inside project root');
27
+ if (!MODULE_EXTENSIONS.has(extname(modulePath)))
28
+ throw new Error('runner module must be .ts, .mts, .js, or .mjs');
29
+ await access(modulePath);
30
+ if (config.cwd !== undefined && isAbsolute(config.cwd))
31
+ throw new Error('runner cwd must be project-relative');
32
+ const cwd = resolve(projectRoot, config.cwd ?? '.');
33
+ if (!inside(projectRoot, cwd))
34
+ throw new Error('runner cwd must stay inside project root');
35
+ await access(cwd);
36
+ return { modulePath, cwd };
37
+ }
38
+ async function invokeHost(projectRoot, runnerId, config, payload) {
39
+ const { modulePath, cwd } = await runnerPaths(projectRoot, config);
40
+ const compiledHost = fileURLToPath(new URL('./runner-host.js', import.meta.url));
41
+ const sourceHost = fileURLToPath(new URL('./runner-host.ts', import.meta.url));
42
+ const hostPath = existsSync(compiledHost) ? compiledHost : sourceHost;
43
+ const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
44
+ const child = fork(hostPath, [], {
45
+ cwd: projectRoot,
46
+ execArgv: [],
47
+ stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
48
+ });
49
+ const { promise, resolve: resolvePromise, reject: rejectPromise } = Promise.withResolvers();
50
+ let settled = false;
51
+ let output = '';
52
+ child.stdout?.on('data', chunk => { output = boundedAppend(output, chunk); });
53
+ child.stderr?.on('data', chunk => { output = boundedAppend(output, chunk); });
54
+ const timer = setTimeout(() => {
55
+ if (settled)
56
+ return;
57
+ settled = true;
58
+ child.kill('SIGKILL');
59
+ rejectPromise(new Error(`runner ${runnerId} timed out after ${timeoutMs}ms${output === '' ? '' : `\n${output}`}`));
60
+ }, timeoutMs + 1_000);
61
+ child.once('message', message => {
62
+ if (settled)
63
+ return;
64
+ settled = true;
65
+ clearTimeout(timer);
66
+ child.kill();
67
+ const response = message;
68
+ if (!response.ok) {
69
+ rejectPromise(new Error(`runner ${runnerId} failed: ${response.error ?? 'unknown error'}${output === '' ? '' : `\n${output}`}`));
70
+ return;
71
+ }
72
+ resolvePromise(response.value);
73
+ });
74
+ child.once('error', error => {
75
+ if (settled)
76
+ return;
77
+ settled = true;
78
+ clearTimeout(timer);
79
+ rejectPromise(error);
80
+ });
81
+ child.once('exit', code => {
82
+ if (settled)
83
+ return;
84
+ settled = true;
85
+ clearTimeout(timer);
86
+ rejectPromise(new Error(`runner ${runnerId} exited before responding with code ${String(code)}${output === '' ? '' : `\n${output}`}`));
87
+ });
88
+ child.send({
89
+ ...payload,
90
+ modulePath,
91
+ projectRoot,
92
+ cwd,
93
+ runnerId,
94
+ options: config.options ?? null,
95
+ timeoutMs,
96
+ });
97
+ return promise;
98
+ }
99
+ function record(value) {
100
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
101
+ ? value
102
+ : undefined;
103
+ }
104
+ function validateTarget(projectRoot, value) {
105
+ const target = record(value);
106
+ if (target === undefined || !text(target.selector) || !text(target.targetId) || !text(target.displayName)) {
107
+ throw new Error('runner returned an invalid resolved target');
108
+ }
109
+ let source;
110
+ if (target.source !== undefined) {
111
+ const location = record(target.source);
112
+ if (location === undefined || !text(location.path) || isAbsolute(location.path))
113
+ throw new Error('runner returned an invalid source path');
114
+ const absolute = resolve(projectRoot, location.path);
115
+ if (!inside(projectRoot, absolute))
116
+ throw new Error('runner source path must stay inside project root');
117
+ if (location.line !== undefined && (!Number.isInteger(location.line) || location.line <= 0)) {
118
+ throw new Error('runner source line must be a positive integer');
119
+ }
120
+ source = { path: location.path, ...(location.line === undefined ? {} : { line: location.line }) };
121
+ }
122
+ if (target.data !== undefined && !isJsonValue(target.data))
123
+ throw new Error('runner target data must be JSON-compatible');
124
+ return {
125
+ selector: target.selector,
126
+ targetId: target.targetId,
127
+ displayName: target.displayName,
128
+ ...(source === undefined ? {} : { source }),
129
+ ...(target.data === undefined ? {} : { data: target.data }),
130
+ };
131
+ }
132
+ export async function resolveRunnerTargets(projectRoot, runnerId, config, selectors) {
133
+ const value = record(await invokeHost(projectRoot, runnerId, config, { operation: 'resolve', selectors }));
134
+ if (value === undefined || !Array.isArray(value.targets) || !Array.isArray(value.errors)) {
135
+ throw new Error(`runner ${runnerId} returned an invalid resolve response`);
136
+ }
137
+ const targets = value.targets.map(item => validateTarget(projectRoot, item));
138
+ const errors = value.errors.map(item => {
139
+ const error = record(item);
140
+ if (error === undefined || !text(error.selector) || !text(error.message))
141
+ throw new Error(`runner ${runnerId} returned an invalid resolve error`);
142
+ return { selector: error.selector, message: error.message };
143
+ });
144
+ const requested = new Set(selectors);
145
+ const outcomes = new Map();
146
+ for (const target of targets) {
147
+ if (!requested.has(target.selector))
148
+ throw new Error(`runner ${runnerId} returned unknown selector ${target.selector}`);
149
+ outcomes.set(target.selector, (outcomes.get(target.selector) ?? 0) + 1);
150
+ }
151
+ for (const error of errors) {
152
+ if (!requested.has(error.selector))
153
+ throw new Error(`runner ${runnerId} returned unknown selector ${error.selector}`);
154
+ outcomes.set(error.selector, (outcomes.get(error.selector) ?? 0) + 1);
155
+ }
156
+ for (const selector of selectors) {
157
+ const count = outcomes.get(selector) ?? 0;
158
+ if (count !== 1)
159
+ throw new Error(`runner ${runnerId} must return exactly one resolution outcome for ${selector}; received ${count}`);
160
+ }
161
+ return { targets, errors };
162
+ }
163
+ export async function runRunnerTargets(projectRoot, runnerId, config, targets) {
164
+ const value = record(await invokeHost(projectRoot, runnerId, config, { operation: 'run', targets }));
165
+ if (value === undefined || !Array.isArray(value.results))
166
+ throw new Error(`runner ${runnerId} returned an invalid run response`);
167
+ const requested = new Set(targets.map(target => target.targetId));
168
+ const seen = new Set();
169
+ const results = value.results.map(item => {
170
+ const result = record(item);
171
+ if (result === undefined || !text(result.targetId) || (result.status !== 'pass' && result.status !== 'fail' && result.status !== 'skip')) {
172
+ throw new Error(`runner ${runnerId} returned an invalid target result`);
173
+ }
174
+ if (!requested.has(result.targetId))
175
+ throw new Error(`runner ${runnerId} returned unknown target ${result.targetId}`);
176
+ if (seen.has(result.targetId))
177
+ throw new Error(`runner ${runnerId} returned duplicate target ${result.targetId}`);
178
+ if (result.diagnostic !== undefined && typeof result.diagnostic !== 'string')
179
+ throw new Error(`runner ${runnerId} returned an invalid diagnostic`);
180
+ seen.add(result.targetId);
181
+ return {
182
+ targetId: result.targetId,
183
+ status: result.status,
184
+ ...(result.diagnostic === undefined ? {} : { diagnostic: result.diagnostic }),
185
+ };
186
+ });
187
+ for (const target of targets) {
188
+ if (!seen.has(target.targetId))
189
+ throw new Error(`runner ${runnerId} did not return target ${target.targetId}`);
190
+ }
191
+ return { results };
192
+ }
193
+ //# sourceMappingURL=runner-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner-client.js","sourceRoot":"","sources":["../src/runner-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ import { pathToFileURL } from 'node:url';
2
+ let handled = false;
3
+ process.on('message', async (message) => {
4
+ if (handled)
5
+ return;
6
+ handled = true;
7
+ const request = message;
8
+ try {
9
+ // The module path is selected by the repository configuration, so a static import cannot represent it.
10
+ const loaded = await import(pathToFileURL(request.modulePath).href);
11
+ const plugin = loaded.default;
12
+ if (plugin === undefined || plugin.apiVersion !== 1 || typeof plugin.resolve !== 'function' || typeof plugin.run !== 'function') {
13
+ throw new Error('runner module must default-export a RunnerPlugin with apiVersion 1');
14
+ }
15
+ const context = {
16
+ projectRoot: request.projectRoot,
17
+ cwd: request.cwd,
18
+ runnerId: request.runnerId,
19
+ options: request.options,
20
+ signal: AbortSignal.timeout(request.timeoutMs),
21
+ };
22
+ const value = request.operation === 'resolve'
23
+ ? await plugin.resolve({ ...context, selectors: request.selectors })
24
+ : await plugin.run({ ...context, targets: request.targets });
25
+ process.send?.({ ok: true, value }, () => process.disconnect());
26
+ }
27
+ catch (error) {
28
+ process.send?.({
29
+ ok: false,
30
+ error: error instanceof Error ? error.stack ?? error.message : String(error),
31
+ }, () => process.disconnect());
32
+ }
33
+ });
34
+ //# sourceMappingURL=runner-host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner-host.js","sourceRoot":"","sources":["../src/runner-host.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AA8BxC,IAAI,OAAO,GAAG,KAAK,CAAA;AACnB,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAC,OAAO,EAAC,EAAE;IACpC,IAAI,OAAO;QAAE,OAAM;IACnB,OAAO,GAAG,IAAI,CAAA;IACd,MAAM,OAAO,GAAG,OAAsB,CAAA;IACtC,IAAI,CAAC;QACH,uGAAuG;QACvG,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAA;QACnE,MAAM,MAAM,GAAG,MAAM,CAAC,OAAmC,CAAA;QACzD,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,UAAU,KAAK,CAAC,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,UAAU,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,UAAU,EAAE,CAAC;YAChI,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAA;QACvF,CAAC;QACD,MAAM,OAAO,GAAG;YACd,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;SAC/C,CAAA;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,KAAK,SAAS;YAC3C,CAAC,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC;YACpE,CAAC,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;QAC9D,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAyB,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAA;IACxF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,EAAE,CAAC;YACb,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SACtD,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAA;IACvD,CAAC;AACH,CAAC,CAAC,CAAA"}
@@ -0,0 +1,5 @@
1
+ import type { FocusedSpecConfig, SpecDocument, Violation } from './model.js';
2
+ export declare function loadCurrentDocuments(projectRoot: string, config: FocusedSpecConfig): Promise<SpecDocument[]>;
3
+ export declare function loadChangeDocuments(projectRoot: string, config: FocusedSpecConfig, changeName: string): Promise<SpecDocument[]>;
4
+ export declare function loadActiveChanges(projectRoot: string, config: FocusedSpecConfig): Promise<ReadonlyMap<string, readonly SpecDocument[]>>;
5
+ export declare function validateSourceSelection(config: FocusedSpecConfig, changeName: string | undefined): Violation[];
@@ -0,0 +1,64 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { relative } from 'node:path';
3
+ import fg from 'fast-glob';
4
+ import { parseFocusedSpecDocument } from './parser.js';
5
+ function portable(path) {
6
+ return path.split('\\').join('/');
7
+ }
8
+ async function documents(projectRoot, patterns) {
9
+ const paths = await fg([...patterns], {
10
+ absolute: true,
11
+ cwd: projectRoot,
12
+ onlyFiles: true,
13
+ unique: true,
14
+ });
15
+ paths.sort();
16
+ return Promise.all(paths.map(async (path) => parseFocusedSpecDocument(portable(relative(projectRoot, path)), await readFile(path, 'utf8'))));
17
+ }
18
+ function openSpecRoot(config) {
19
+ if (config.specifications.source !== 'openspec')
20
+ throw new Error('OpenSpec source required');
21
+ return config.specifications.root ?? 'openspec';
22
+ }
23
+ export async function loadCurrentDocuments(projectRoot, config) {
24
+ if (config.specifications.source === 'files')
25
+ return documents(projectRoot, config.specifications.paths);
26
+ return documents(projectRoot, [`${openSpecRoot(config)}/specs/**/spec.md`]);
27
+ }
28
+ export async function loadChangeDocuments(projectRoot, config, changeName) {
29
+ if (config.specifications.source !== 'openspec')
30
+ return [];
31
+ return documents(projectRoot, [`${openSpecRoot(config)}/changes/${changeName}/specs/**/spec.md`]);
32
+ }
33
+ export async function loadActiveChanges(projectRoot, config) {
34
+ if (config.specifications.source !== 'openspec')
35
+ return new Map();
36
+ const root = openSpecRoot(config);
37
+ const paths = await fg([`${root}/changes/*/specs/**/spec.md`, `!${root}/changes/archive/**`], {
38
+ absolute: true,
39
+ cwd: projectRoot,
40
+ onlyFiles: true,
41
+ unique: true,
42
+ });
43
+ const groups = new Map();
44
+ for (const path of paths.sort()) {
45
+ const rel = portable(relative(projectRoot, path));
46
+ const prefix = `${portable(root)}/changes/`;
47
+ const changeName = rel.slice(prefix.length).split('/')[0];
48
+ if (changeName === undefined || changeName === '')
49
+ continue;
50
+ groups.set(changeName, [...(groups.get(changeName) ?? []), path]);
51
+ }
52
+ const result = new Map();
53
+ for (const [name, changePaths] of groups) {
54
+ result.set(name, await Promise.all(changePaths.map(async (path) => parseFocusedSpecDocument(portable(relative(projectRoot, path)), await readFile(path, 'utf8')))));
55
+ }
56
+ return result;
57
+ }
58
+ export function validateSourceSelection(config, changeName) {
59
+ if (changeName !== undefined && config.specifications.source !== 'openspec') {
60
+ return [{ path: '.focused-spec/config.yaml', message: '--change requires specifications.source: openspec' }];
61
+ }
62
+ return [];
63
+ }
64
+ //# sourceMappingURL=sources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sources.js","sourceRoot":"","sources":["../src/sources.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { FocusedSpecConfig, Scenario, SpecDocument, Violation } from './model.js';
2
+ interface DocumentValidation {
3
+ readonly violations: readonly Violation[];
4
+ readonly identifiers: ReadonlyMap<string, Scenario>;
5
+ }
6
+ export declare function validateDocuments(documents: readonly SpecDocument[], config: FocusedSpecConfig, options: {
7
+ readonly allowPlanned: boolean;
8
+ }): DocumentValidation;
9
+ export interface ValidationOptions {
10
+ readonly changeName?: string;
11
+ readonly strict?: boolean;
12
+ }
13
+ export interface ValidationOutput {
14
+ readonly violations: readonly Violation[];
15
+ readonly targetDocuments: readonly SpecDocument[];
16
+ readonly resolutionDocuments: readonly SpecDocument[];
17
+ }
18
+ export declare function validateFocusedSpecs(projectRoot: string, config: FocusedSpecConfig, options?: ValidationOptions): Promise<ValidationOutput>;
19
+ export {};