@deckspec/dsl 0.1.0 → 0.1.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.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/parser.d.ts +35 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +90 -0
- package/dist/parser.js.map +1 -0
- package/dist/scanner.d.ts +23 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +77 -0
- package/dist/scanner.js.map +1 -0
- package/dist/state.d.ts +28 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +116 -0
- package/dist/state.js.map +1 -0
- package/dist/types.d.ts +24 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/validator.d.ts +18 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +102 -0
- package/dist/validator.js.map +1 -0
- package/package.json +6 -2
- package/tsconfig.json +0 -13
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { SlideValidationResult, ValidationResult, } from "./types.js";
|
|
2
|
+
export { parseDeckYaml, loadDeckFile, resolveSlideFile, type ResolvedSlideFile, } from "./parser.js";
|
|
3
|
+
export { validateDeck, type ValidationContext } from "./validator.js";
|
|
4
|
+
export { approveSlide, rejectSlide, archiveDeck, activateDeck, lockSlide, } from "./state.js";
|
|
5
|
+
export { scanDecks, type DeckSummary, type SlideSummary } from "./scanner.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,KAAK,iBAAiB,GACvB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGtE,OAAO,EACL,YAAY,EACZ,WAAW,EACX,WAAW,EACX,YAAY,EACZ,SAAS,GACV,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Parser
|
|
2
|
+
export { parseDeckYaml, loadDeckFile, resolveSlideFile, } from "./parser.js";
|
|
3
|
+
// Validator
|
|
4
|
+
export { validateDeck } from "./validator.js";
|
|
5
|
+
// State mutations
|
|
6
|
+
export { approveSlide, rejectSlide, archiveDeck, activateDeck, lockSlide, } from "./state.js";
|
|
7
|
+
// Scanner
|
|
8
|
+
export { scanDecks } from "./scanner.js";
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,SAAS;AACT,OAAO,EACL,aAAa,EACb,YAAY,EACZ,gBAAgB,GAEjB,MAAM,aAAa,CAAC;AAErB,YAAY;AACZ,OAAO,EAAE,YAAY,EAA0B,MAAM,gBAAgB,CAAC;AAEtE,kBAAkB;AAClB,OAAO,EACL,YAAY,EACZ,WAAW,EACX,WAAW,EACX,YAAY,EACZ,SAAS,GACV,MAAM,YAAY,CAAC;AAEpB,UAAU;AACV,OAAO,EAAE,SAAS,EAAuC,MAAM,cAAc,CAAC"}
|
package/dist/parser.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a YAML string into a raw JavaScript object.
|
|
3
|
+
* Does not perform any schema validation — returns the parsed value as-is.
|
|
4
|
+
*
|
|
5
|
+
* @throws Error with a descriptive message if YAML parsing fails.
|
|
6
|
+
*/
|
|
7
|
+
export declare function parseDeckYaml(yamlString: string): unknown;
|
|
8
|
+
/**
|
|
9
|
+
* Reads a deck YAML file from disk and parses it.
|
|
10
|
+
*
|
|
11
|
+
* @throws Error if the file cannot be read or the YAML is malformed.
|
|
12
|
+
*/
|
|
13
|
+
export declare function loadDeckFile(filePath: string): Promise<unknown>;
|
|
14
|
+
/**
|
|
15
|
+
* Resolved slide file reference.
|
|
16
|
+
*/
|
|
17
|
+
export interface ResolvedSlideFile {
|
|
18
|
+
type: "html" | "pattern";
|
|
19
|
+
path: string;
|
|
20
|
+
/** True when the resolved pattern is a .tsx source file (needs on-the-fly compilation) */
|
|
21
|
+
tsx?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolves a slide `file:` value to an absolute path and type.
|
|
25
|
+
*
|
|
26
|
+
* Resolution priority (ADR-022):
|
|
27
|
+
* 1. Deck-local pattern: {basePath}/patterns/{name}/index.tsx
|
|
28
|
+
* 2. Deck-local pattern: {basePath}/patterns/{name}/index.js
|
|
29
|
+
* 3. Theme pattern: {patternsDir}/{name}/index.js
|
|
30
|
+
* 4. Theme pattern: {patternsDir}/{name}.js
|
|
31
|
+
*
|
|
32
|
+
* Files with extensions (.html etc) are resolved relative to basePath as passthrough.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveSlideFile(file: string, basePath: string, patternsDir: string): Promise<ResolvedSlideFile>;
|
|
35
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAQzD;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUrE;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,0FAA0F;IAC1F,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CA6C5B"}
|
package/dist/parser.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import yaml from "js-yaml";
|
|
2
|
+
import { readFile, access } from "node:fs/promises";
|
|
3
|
+
import { resolve, extname } from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* Parses a YAML string into a raw JavaScript object.
|
|
6
|
+
* Does not perform any schema validation — returns the parsed value as-is.
|
|
7
|
+
*
|
|
8
|
+
* @throws Error with a descriptive message if YAML parsing fails.
|
|
9
|
+
*/
|
|
10
|
+
export function parseDeckYaml(yamlString) {
|
|
11
|
+
try {
|
|
12
|
+
return yaml.load(yamlString);
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
const message = error instanceof Error ? error.message : "Unknown YAML parse error";
|
|
16
|
+
throw new Error(`Failed to parse deck YAML: ${message}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Reads a deck YAML file from disk and parses it.
|
|
21
|
+
*
|
|
22
|
+
* @throws Error if the file cannot be read or the YAML is malformed.
|
|
23
|
+
*/
|
|
24
|
+
export async function loadDeckFile(filePath) {
|
|
25
|
+
let content;
|
|
26
|
+
try {
|
|
27
|
+
content = await readFile(filePath, "utf-8");
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
const message = error instanceof Error ? error.message : "Unknown file read error";
|
|
31
|
+
throw new Error(`Failed to read deck file "${filePath}": ${message}`);
|
|
32
|
+
}
|
|
33
|
+
return parseDeckYaml(content);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolves a slide `file:` value to an absolute path and type.
|
|
37
|
+
*
|
|
38
|
+
* Resolution priority (ADR-022):
|
|
39
|
+
* 1. Deck-local pattern: {basePath}/patterns/{name}/index.tsx
|
|
40
|
+
* 2. Deck-local pattern: {basePath}/patterns/{name}/index.js
|
|
41
|
+
* 3. Theme pattern: {patternsDir}/{name}/index.js
|
|
42
|
+
* 4. Theme pattern: {patternsDir}/{name}.js
|
|
43
|
+
*
|
|
44
|
+
* Files with extensions (.html etc) are resolved relative to basePath as passthrough.
|
|
45
|
+
*/
|
|
46
|
+
export async function resolveSlideFile(file, basePath, patternsDir) {
|
|
47
|
+
const ext = extname(file);
|
|
48
|
+
if (ext) {
|
|
49
|
+
// File with extension → relative to deck directory
|
|
50
|
+
const fullPath = resolve(basePath, file);
|
|
51
|
+
return { type: "html", path: fullPath };
|
|
52
|
+
}
|
|
53
|
+
// No extension → pattern name
|
|
54
|
+
// 1. Deck-local: {basePath}/patterns/{name}/index.tsx
|
|
55
|
+
const localTsxPath = resolve(basePath, "patterns", file, "index.tsx");
|
|
56
|
+
try {
|
|
57
|
+
await access(localTsxPath);
|
|
58
|
+
return { type: "pattern", path: localTsxPath, tsx: true };
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// continue
|
|
62
|
+
}
|
|
63
|
+
// 2. Deck-local: {basePath}/patterns/{name}/index.js
|
|
64
|
+
const localJsPath = resolve(basePath, "patterns", file, "index.js");
|
|
65
|
+
try {
|
|
66
|
+
await access(localJsPath);
|
|
67
|
+
return { type: "pattern", path: localJsPath };
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// continue
|
|
71
|
+
}
|
|
72
|
+
// 3. Theme: patterns/{name}/index.js (directory)
|
|
73
|
+
const dirIndexPath = resolve(patternsDir, file, "index.js");
|
|
74
|
+
try {
|
|
75
|
+
await access(dirIndexPath);
|
|
76
|
+
return { type: "pattern", path: dirIndexPath };
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// 4. Theme: patterns/{name}.js (flat file)
|
|
80
|
+
const flatPath = resolve(patternsDir, `${file}.js`);
|
|
81
|
+
try {
|
|
82
|
+
await access(flatPath);
|
|
83
|
+
return { type: "pattern", path: flatPath };
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
throw new Error(`Pattern "${file}" not found. Looked in:\n ${localTsxPath}\n ${localJsPath}\n ${dirIndexPath}\n ${flatPath}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# 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,23 @@
|
|
|
1
|
+
export interface SlideSummary {
|
|
2
|
+
index: number;
|
|
3
|
+
state: string;
|
|
4
|
+
/** Label extracted from first <h1> or <h2> in the HTML */
|
|
5
|
+
label?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface DeckSummary {
|
|
8
|
+
filePath: string;
|
|
9
|
+
relativePath: string;
|
|
10
|
+
meta: {
|
|
11
|
+
title: string;
|
|
12
|
+
theme: string;
|
|
13
|
+
state: string;
|
|
14
|
+
};
|
|
15
|
+
slideCount: number;
|
|
16
|
+
slideSummaries: SlideSummary[];
|
|
17
|
+
approvedCount: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Scans a directory recursively for deck.yaml files and returns summaries.
|
|
21
|
+
*/
|
|
22
|
+
export declare function scanDecks(dir: string): Promise<DeckSummary[]>;
|
|
23
|
+
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,YAAY,EAAE,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;CACvB;AAgCD;;GAEG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAqDnE"}
|
package/dist/scanner.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { join, relative } from "node:path";
|
|
3
|
+
import yaml from "js-yaml";
|
|
4
|
+
const HEADING_RE = /<h[12][^>]*>(.*?)<\/h[12]>/i;
|
|
5
|
+
function extractLabel(html) {
|
|
6
|
+
const match = HEADING_RE.exec(html);
|
|
7
|
+
if (match) {
|
|
8
|
+
// Strip any nested HTML tags from the heading content
|
|
9
|
+
return match[1].replace(/<[^>]*>/g, "").trim() || undefined;
|
|
10
|
+
}
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
async function findDeckFiles(dir) {
|
|
14
|
+
const results = [];
|
|
15
|
+
async function walk(current) {
|
|
16
|
+
const entries = await readdir(current, { withFileTypes: true });
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
const fullPath = join(current, entry.name);
|
|
19
|
+
if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".git") {
|
|
20
|
+
await walk(fullPath);
|
|
21
|
+
}
|
|
22
|
+
else if (entry.isFile() && entry.name === "deck.yaml") {
|
|
23
|
+
results.push(fullPath);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
await walk(dir);
|
|
28
|
+
return results.sort();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Scans a directory recursively for deck.yaml files and returns summaries.
|
|
32
|
+
*/
|
|
33
|
+
export async function scanDecks(dir) {
|
|
34
|
+
const files = await findDeckFiles(dir);
|
|
35
|
+
const summaries = [];
|
|
36
|
+
for (const filePath of files) {
|
|
37
|
+
try {
|
|
38
|
+
const content = await readFile(filePath, "utf-8");
|
|
39
|
+
const raw = yaml.load(content);
|
|
40
|
+
if (!raw?.meta || !raw?.slides || !Array.isArray(raw.slides)) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const slideSummaries = raw.slides.map((slide, index) => {
|
|
44
|
+
const state = String(slide.state ?? "generated");
|
|
45
|
+
// For file-based slides: use file name as label fallback
|
|
46
|
+
const file = typeof slide.file === "string" ? slide.file : undefined;
|
|
47
|
+
// Try extracting from vars (pattern slides)
|
|
48
|
+
const vars = slide.vars;
|
|
49
|
+
const varsTitle = vars?.title ?? vars?.headline;
|
|
50
|
+
const label = typeof varsTitle === "string"
|
|
51
|
+
? varsTitle
|
|
52
|
+
: file
|
|
53
|
+
? file.replace(/\.[^.]+$/, "").split("/").pop()
|
|
54
|
+
: undefined;
|
|
55
|
+
return { index, state, label };
|
|
56
|
+
});
|
|
57
|
+
const approvedCount = slideSummaries.filter((s) => s.state === "approved" || s.state === "locked").length;
|
|
58
|
+
summaries.push({
|
|
59
|
+
filePath,
|
|
60
|
+
relativePath: relative(dir, filePath),
|
|
61
|
+
meta: {
|
|
62
|
+
title: String(raw.meta.title ?? "Untitled"),
|
|
63
|
+
theme: String(raw.meta.theme ?? "noir-display"),
|
|
64
|
+
state: String(raw.meta.state ?? "active"),
|
|
65
|
+
},
|
|
66
|
+
slideCount: raw.slides.length,
|
|
67
|
+
slideSummaries,
|
|
68
|
+
approvedCount,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Skip files that can't be parsed
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return summaries;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"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"}
|
package/dist/state.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approve a slide — sets its state to "approved".
|
|
3
|
+
*/
|
|
4
|
+
export declare function approveSlide(filePath: string, slideIndex: number): Promise<void>;
|
|
5
|
+
/**
|
|
6
|
+
* Reject a slide — resets its state to "generated".
|
|
7
|
+
* Only allowed from "approved" state.
|
|
8
|
+
*/
|
|
9
|
+
export declare function rejectSlide(filePath: string, slideIndex: number): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Archive a deck — sets meta.state to "archived".
|
|
12
|
+
*/
|
|
13
|
+
export declare function archiveDeck(filePath: string): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Activate a deck — sets meta.state to "active".
|
|
16
|
+
*/
|
|
17
|
+
export declare function activateDeck(filePath: string): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Lock a slide — sets state to "locked" and rewrites it as a pattern reference.
|
|
20
|
+
* The slide must be in "approved" state.
|
|
21
|
+
*
|
|
22
|
+
* @param filePath - Path to the deck YAML file.
|
|
23
|
+
* @param slideIndex - Zero-based index of the slide to lock.
|
|
24
|
+
* @param patternName - Name for the new pattern.
|
|
25
|
+
* @param vars - Variables extracted from the slide.
|
|
26
|
+
*/
|
|
27
|
+
export declare function lockSlide(filePath: string, slideIndex: number, patternName: string, vars: Record<string, unknown>): Promise<void>;
|
|
28
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAmDA;;GAEG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAef;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQjE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQlE;AAED;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,IAAI,CAAC,CAkBf"}
|
package/dist/state.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import yaml from "js-yaml";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
/**
|
|
4
|
+
* Valid slide state transitions.
|
|
5
|
+
* Only these transitions are allowed:
|
|
6
|
+
* generated → approved
|
|
7
|
+
* derived → approved
|
|
8
|
+
* approved → locked
|
|
9
|
+
*/
|
|
10
|
+
const VALID_SLIDE_TRANSITIONS = {
|
|
11
|
+
generated: ["approved"],
|
|
12
|
+
derived: ["approved"],
|
|
13
|
+
approved: ["locked"],
|
|
14
|
+
locked: [],
|
|
15
|
+
};
|
|
16
|
+
async function loadRawYaml(filePath) {
|
|
17
|
+
const content = await readFile(filePath, "utf-8");
|
|
18
|
+
const raw = yaml.load(content);
|
|
19
|
+
if (!raw?.slides || !Array.isArray(raw.slides)) {
|
|
20
|
+
throw new Error(`Invalid deck file: ${filePath}`);
|
|
21
|
+
}
|
|
22
|
+
return { raw, content };
|
|
23
|
+
}
|
|
24
|
+
async function saveYaml(filePath, data) {
|
|
25
|
+
const output = yaml.dump(data, {
|
|
26
|
+
lineWidth: -1,
|
|
27
|
+
noRefs: true,
|
|
28
|
+
quotingType: '"',
|
|
29
|
+
forceQuotes: false,
|
|
30
|
+
});
|
|
31
|
+
await writeFile(filePath, output, "utf-8");
|
|
32
|
+
}
|
|
33
|
+
function validateSlideTransition(from, to) {
|
|
34
|
+
const allowed = VALID_SLIDE_TRANSITIONS[from];
|
|
35
|
+
if (!allowed || !allowed.includes(to)) {
|
|
36
|
+
throw new Error(`Invalid state transition: "${from}" → "${to}". Allowed from "${from}": [${(allowed ?? []).join(", ")}]`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Approve a slide — sets its state to "approved".
|
|
41
|
+
*/
|
|
42
|
+
export async function approveSlide(filePath, slideIndex) {
|
|
43
|
+
const { raw } = await loadRawYaml(filePath);
|
|
44
|
+
if (slideIndex < 0 || slideIndex >= raw.slides.length) {
|
|
45
|
+
throw new Error(`Slide index ${slideIndex} out of range (0–${raw.slides.length - 1})`);
|
|
46
|
+
}
|
|
47
|
+
const current = (raw.slides[slideIndex].state ?? "generated");
|
|
48
|
+
validateSlideTransition(current, "approved");
|
|
49
|
+
raw.slides[slideIndex].state = "approved";
|
|
50
|
+
await saveYaml(filePath, raw);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Reject a slide — resets its state to "generated".
|
|
54
|
+
* Only allowed from "approved" state.
|
|
55
|
+
*/
|
|
56
|
+
export async function rejectSlide(filePath, slideIndex) {
|
|
57
|
+
const { raw } = await loadRawYaml(filePath);
|
|
58
|
+
if (slideIndex < 0 || slideIndex >= raw.slides.length) {
|
|
59
|
+
throw new Error(`Slide index ${slideIndex} out of range (0–${raw.slides.length - 1})`);
|
|
60
|
+
}
|
|
61
|
+
const current = (raw.slides[slideIndex].state ?? "generated");
|
|
62
|
+
if (current !== "approved") {
|
|
63
|
+
throw new Error(`Cannot reject slide in "${current}" state. Only "approved" slides can be rejected.`);
|
|
64
|
+
}
|
|
65
|
+
raw.slides[slideIndex].state = "generated";
|
|
66
|
+
await saveYaml(filePath, raw);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Archive a deck — sets meta.state to "archived".
|
|
70
|
+
*/
|
|
71
|
+
export async function archiveDeck(filePath) {
|
|
72
|
+
const { raw } = await loadRawYaml(filePath);
|
|
73
|
+
const current = (raw.meta.state ?? "active");
|
|
74
|
+
if (current === "archived") {
|
|
75
|
+
throw new Error("Deck is already archived.");
|
|
76
|
+
}
|
|
77
|
+
raw.meta.state = "archived";
|
|
78
|
+
await saveYaml(filePath, raw);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Activate a deck — sets meta.state to "active".
|
|
82
|
+
*/
|
|
83
|
+
export async function activateDeck(filePath) {
|
|
84
|
+
const { raw } = await loadRawYaml(filePath);
|
|
85
|
+
const current = (raw.meta.state ?? "active");
|
|
86
|
+
if (current === "active") {
|
|
87
|
+
throw new Error("Deck is already active.");
|
|
88
|
+
}
|
|
89
|
+
raw.meta.state = "active";
|
|
90
|
+
await saveYaml(filePath, raw);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Lock a slide — sets state to "locked" and rewrites it as a pattern reference.
|
|
94
|
+
* The slide must be in "approved" state.
|
|
95
|
+
*
|
|
96
|
+
* @param filePath - Path to the deck YAML file.
|
|
97
|
+
* @param slideIndex - Zero-based index of the slide to lock.
|
|
98
|
+
* @param patternName - Name for the new pattern.
|
|
99
|
+
* @param vars - Variables extracted from the slide.
|
|
100
|
+
*/
|
|
101
|
+
export async function lockSlide(filePath, slideIndex, patternName, vars) {
|
|
102
|
+
const { raw } = await loadRawYaml(filePath);
|
|
103
|
+
if (slideIndex < 0 || slideIndex >= raw.slides.length) {
|
|
104
|
+
throw new Error(`Slide index ${slideIndex} out of range (0–${raw.slides.length - 1})`);
|
|
105
|
+
}
|
|
106
|
+
const current = (raw.slides[slideIndex].state ?? "generated");
|
|
107
|
+
validateSlideTransition(current, "locked");
|
|
108
|
+
// Rewrite the slide entry
|
|
109
|
+
raw.slides[slideIndex] = {
|
|
110
|
+
file: patternName,
|
|
111
|
+
state: "locked",
|
|
112
|
+
vars,
|
|
113
|
+
};
|
|
114
|
+
await saveYaml(filePath, raw);
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ZodError } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Validation result for a single slide.
|
|
4
|
+
*/
|
|
5
|
+
export interface SlideValidationResult {
|
|
6
|
+
/** Zero-based index of the slide in the deck. */
|
|
7
|
+
index: number;
|
|
8
|
+
/** Whether the slide passed validation. */
|
|
9
|
+
valid: boolean;
|
|
10
|
+
/** Zod validation errors, present only when valid is false. */
|
|
11
|
+
errors?: ZodError;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Validation result for an entire deck.
|
|
15
|
+
*/
|
|
16
|
+
export interface ValidationResult {
|
|
17
|
+
/** Whether all slides (and deck structure) passed validation. */
|
|
18
|
+
valid: boolean;
|
|
19
|
+
/** Per-slide validation results. */
|
|
20
|
+
results: SlideValidationResult[];
|
|
21
|
+
/** Deck-level structural error, if any. */
|
|
22
|
+
deckError?: ZodError;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAEpC;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,KAAK,EAAE,OAAO,CAAC;IACf,+DAA+D;IAC/D,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iEAAiE;IACjE,KAAK,EAAE,OAAO,CAAC;IACf,oCAAoC;IACpC,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,2CAA2C;IAC3C,SAAS,CAAC,EAAE,QAAQ,CAAC;CACtB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ValidationResult } from "./types.js";
|
|
2
|
+
export interface ValidationContext {
|
|
3
|
+
basePath: string;
|
|
4
|
+
patternsDir: string;
|
|
5
|
+
/** Optional function to compile .tsx files on-the-fly. Provided by renderer. */
|
|
6
|
+
compileTsx?: (tsxPath: string) => Promise<string>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Validates a raw parsed deck object.
|
|
10
|
+
*
|
|
11
|
+
* Validates the overall deck structure (meta + slides array) with deckSchema.
|
|
12
|
+
* For each slide:
|
|
13
|
+
* - .html files: checks file exists on disk
|
|
14
|
+
* - pattern names: imports compiled .js, validates vars against exported schema
|
|
15
|
+
* - .tsx patterns (deck-local): compiled via compileTsx if provided, otherwise skips vars validation
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateDeck(raw: unknown, context?: ValidationContext): Promise<ValidationResult>;
|
|
18
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAyB,MAAM,YAAY,CAAC;AAG1E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACnD;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,OAAO,EACZ,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,gBAAgB,CAAC,CAkG3B"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { deckSchema } from "@deckspec/schema";
|
|
4
|
+
import { resolveSlideFile } from "./parser.js";
|
|
5
|
+
/**
|
|
6
|
+
* Validates a raw parsed deck object.
|
|
7
|
+
*
|
|
8
|
+
* Validates the overall deck structure (meta + slides array) with deckSchema.
|
|
9
|
+
* For each slide:
|
|
10
|
+
* - .html files: checks file exists on disk
|
|
11
|
+
* - pattern names: imports compiled .js, validates vars against exported schema
|
|
12
|
+
* - .tsx patterns (deck-local): compiled via compileTsx if provided, otherwise skips vars validation
|
|
13
|
+
*/
|
|
14
|
+
export async function validateDeck(raw, context) {
|
|
15
|
+
const deckResult = deckSchema.safeParse(raw);
|
|
16
|
+
if (!deckResult.success) {
|
|
17
|
+
return {
|
|
18
|
+
valid: false,
|
|
19
|
+
results: [],
|
|
20
|
+
deckError: deckResult.error,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const deck = deckResult.data;
|
|
24
|
+
const results = [];
|
|
25
|
+
let allValid = true;
|
|
26
|
+
for (let index = 0; index < deck.slides.length; index++) {
|
|
27
|
+
const slide = deck.slides[index];
|
|
28
|
+
// If no context provided, only do structural validation
|
|
29
|
+
if (!context) {
|
|
30
|
+
results.push({ index, valid: true });
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const resolved = await resolveSlideFile(slide.file, context.basePath, context.patternsDir);
|
|
35
|
+
if (resolved.type === "html") {
|
|
36
|
+
// Check file exists
|
|
37
|
+
await access(resolved.path);
|
|
38
|
+
results.push({ index, valid: true });
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// Pattern: import module and validate vars
|
|
42
|
+
let modulePath = resolved.path;
|
|
43
|
+
if (resolved.tsx && context.compileTsx) {
|
|
44
|
+
modulePath = await context.compileTsx(resolved.path);
|
|
45
|
+
}
|
|
46
|
+
else if (resolved.tsx) {
|
|
47
|
+
// No compiler provided — just check file exists (structural validation only)
|
|
48
|
+
await access(resolved.path);
|
|
49
|
+
results.push({ index, valid: true });
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const mod = await import(modulePath);
|
|
53
|
+
// Check asset file existence
|
|
54
|
+
if (mod.assets && Array.isArray(mod.assets) && slide.vars) {
|
|
55
|
+
for (const spec of mod.assets) {
|
|
56
|
+
const val = slide.vars[spec.field];
|
|
57
|
+
if (typeof val === "string" && !val.startsWith("https://")) {
|
|
58
|
+
await access(resolve(context.basePath, val));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (mod.schema && slide.vars) {
|
|
63
|
+
const parseResult = mod.schema.safeParse(slide.vars);
|
|
64
|
+
if (!parseResult.success) {
|
|
65
|
+
results.push({ index, valid: false, errors: parseResult.error });
|
|
66
|
+
allValid = false;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (mod.schema && !slide.vars) {
|
|
71
|
+
// Check if schema has required fields
|
|
72
|
+
const parseResult = mod.schema.safeParse({});
|
|
73
|
+
if (!parseResult.success) {
|
|
74
|
+
results.push({ index, valid: false, errors: parseResult.error });
|
|
75
|
+
allValid = false;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
results.push({ index, valid: true });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
84
|
+
// Create a synthetic ZodError-like structure for file resolution errors
|
|
85
|
+
const { ZodError, ZodIssueCode } = await import("zod");
|
|
86
|
+
const zodError = new ZodError([
|
|
87
|
+
{
|
|
88
|
+
code: ZodIssueCode.custom,
|
|
89
|
+
path: ["slides", index, "file"],
|
|
90
|
+
message,
|
|
91
|
+
},
|
|
92
|
+
]);
|
|
93
|
+
results.push({ index, valid: false, errors: zodError });
|
|
94
|
+
allValid = false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
valid: allValid,
|
|
99
|
+
results,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deckspec/dsl",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist",
|
|
6
|
+
"src"
|
|
7
|
+
],
|
|
4
8
|
"type": "module",
|
|
5
9
|
"main": "./dist/index.js",
|
|
6
10
|
"types": "./dist/index.d.ts",
|
|
7
11
|
"dependencies": {
|
|
8
12
|
"js-yaml": "^4.1.0",
|
|
9
13
|
"zod": "^3.23.0",
|
|
10
|
-
"@deckspec/schema": "0.1.
|
|
14
|
+
"@deckspec/schema": "0.1.1"
|
|
11
15
|
},
|
|
12
16
|
"devDependencies": {
|
|
13
17
|
"@types/js-yaml": "^4.0.9",
|
package/tsconfig.json
DELETED