@uurtech/jdf-cli 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.js ADDED
@@ -0,0 +1,331 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import Ajv from 'ajv';
6
+ import addFormats from 'ajv-formats';
7
+
8
+ var __dirname$1 = path.dirname(fileURLToPath(import.meta.url));
9
+ function resolveSchemaPath() {
10
+ const bundled = path.resolve(__dirname$1, "jdf-schema.json");
11
+ if (fs.existsSync(bundled)) return bundled;
12
+ const dev = path.resolve(__dirname$1, "../../../../spec/jdf-schema.json");
13
+ return dev;
14
+ }
15
+ var SCHEMA_PATH = resolveSchemaPath();
16
+ async function validate(file) {
17
+ const filePath = path.resolve(file);
18
+ if (!fs.existsSync(filePath)) {
19
+ console.error(`File not found: ${filePath}`);
20
+ return false;
21
+ }
22
+ let doc;
23
+ try {
24
+ doc = JSON.parse(fs.readFileSync(filePath, "utf-8"));
25
+ } catch (e) {
26
+ console.error(`Invalid JSON: ${e.message}`);
27
+ return false;
28
+ }
29
+ if (!fs.existsSync(SCHEMA_PATH)) {
30
+ console.error(`Schema not found at ${SCHEMA_PATH}`);
31
+ return false;
32
+ }
33
+ const schema = JSON.parse(fs.readFileSync(SCHEMA_PATH, "utf-8"));
34
+ const ajv = new Ajv({ allErrors: true, strict: false });
35
+ addFormats(ajv);
36
+ const validateFn = ajv.compile(schema);
37
+ const ok = validateFn(doc);
38
+ if (ok) {
39
+ const d = doc;
40
+ const pageCount = Array.isArray(d.pages) ? d.pages.length : 0;
41
+ const elCount = Array.isArray(d.pages) ? d.pages.reduce((acc, p) => acc + (Array.isArray(p?.elements) ? p.elements.length : 0), 0) : 0;
42
+ console.log(`\u2713 Valid: ${path.basename(filePath)}`);
43
+ console.log(` Format: ${d.$jdf}`);
44
+ console.log(` Title: ${d.meta?.title}`);
45
+ console.log(` Pages: ${pageCount}`);
46
+ console.log(` Elements: ${elCount}`);
47
+ return true;
48
+ }
49
+ console.error(`\u2717 Invalid: ${path.basename(filePath)}`);
50
+ for (const err of validateFn.errors || []) {
51
+ const loc = err.instancePath || "(root)";
52
+ console.error(` ${loc} \u2014 ${err.message}`);
53
+ }
54
+ return false;
55
+ }
56
+ async function importMarkdown(inputPath, outputPath) {
57
+ const input = path.resolve(inputPath);
58
+ const output = outputPath ? path.resolve(outputPath) : input.replace(/\.(md|markdown)$/i, ".jdf");
59
+ console.log(`Importing: ${input}`);
60
+ console.log(`Output: ${output}`);
61
+ const content = fs.readFileSync(input, "utf-8");
62
+ const doc = convertMarkdownToJdf(content, path.basename(input, path.extname(input)));
63
+ fs.writeFileSync(output, JSON.stringify(doc, null, 2));
64
+ console.log(`
65
+ Done! Created ${doc.pages.length} page(s)`);
66
+ console.log(`Open with: open -a "JDF Reader" "${output}"`);
67
+ }
68
+ function convertMarkdownToJdf(md, title) {
69
+ const maxY = 247;
70
+ const contentWidth = 166;
71
+ const pages = [];
72
+ let elements = [];
73
+ let y = 5;
74
+ let pageNum = 1;
75
+ const lines = md.split("\n");
76
+ let i = 0;
77
+ while (i < lines.length) {
78
+ const line = lines[i];
79
+ const headingMatch = line.match(/^(#{1,6})\s+(.+)/);
80
+ if (headingMatch) {
81
+ const level = headingMatch[1].length;
82
+ const text = headingMatch[2].trim();
83
+ const fontSize = level === 1 ? 22 : level === 2 ? 16 : level === 3 ? 13 : 11;
84
+ const height2 = fontSize * 0.6;
85
+ if (y + height2 > maxY) {
86
+ pages.push({ id: `page-${pageNum}`, elements });
87
+ elements = [];
88
+ y = 5;
89
+ pageNum++;
90
+ }
91
+ if (y > 10 && level <= 2) {
92
+ elements.push({ type: "shape", shape: "rect", position: { x: 0, y }, width: contentWidth, height: 0.3, fill: "#e2e8f0" });
93
+ y += 4;
94
+ }
95
+ elements.push({
96
+ type: "text",
97
+ content: text,
98
+ position: { x: 0, y },
99
+ width: contentWidth,
100
+ heading: true,
101
+ tocEntry: text,
102
+ style: { fontFamily: "Inter", fontSize, fontWeight: "bold", color: level <= 2 ? "#0f172a" : "#1e293b" }
103
+ });
104
+ y += height2 + 4;
105
+ i++;
106
+ continue;
107
+ }
108
+ if (line.match(/^\s*[-*+]\s/)) {
109
+ const items = [];
110
+ while (i < lines.length && lines[i].match(/^\s*[-*+]\s/)) {
111
+ items.push({ content: lines[i].replace(/^\s*[-*+]\s+/, "").trim() });
112
+ i++;
113
+ }
114
+ const height2 = items.length * 5 + 3;
115
+ if (y + height2 > maxY) {
116
+ pages.push({ id: `page-${pageNum}`, elements });
117
+ elements = [];
118
+ y = 5;
119
+ pageNum++;
120
+ }
121
+ elements.push({
122
+ type: "list",
123
+ listType: "unordered",
124
+ position: { x: 0, y },
125
+ width: contentWidth,
126
+ style: { fontFamily: "Inter", fontSize: 10, lineHeight: 1.6, color: "#334155" },
127
+ items
128
+ });
129
+ y += height2;
130
+ continue;
131
+ }
132
+ if (line.match(/^\s*\d+\.\s/)) {
133
+ const items = [];
134
+ while (i < lines.length && lines[i].match(/^\s*\d+\.\s/)) {
135
+ items.push({ content: lines[i].replace(/^\s*\d+\.\s+/, "").trim() });
136
+ i++;
137
+ }
138
+ const height2 = items.length * 5 + 3;
139
+ if (y + height2 > maxY) {
140
+ pages.push({ id: `page-${pageNum}`, elements });
141
+ elements = [];
142
+ y = 5;
143
+ pageNum++;
144
+ }
145
+ elements.push({
146
+ type: "list",
147
+ listType: "ordered",
148
+ position: { x: 0, y },
149
+ width: contentWidth,
150
+ style: { fontFamily: "Inter", fontSize: 10, lineHeight: 1.6, color: "#334155" },
151
+ items
152
+ });
153
+ y += height2;
154
+ continue;
155
+ }
156
+ if (line.startsWith("```")) {
157
+ const codeLines = [];
158
+ i++;
159
+ while (i < lines.length && !lines[i].startsWith("```")) {
160
+ codeLines.push(lines[i]);
161
+ i++;
162
+ }
163
+ i++;
164
+ const code = codeLines.join("\n");
165
+ const height2 = codeLines.length * 4 + 8;
166
+ if (y + height2 > maxY) {
167
+ pages.push({ id: `page-${pageNum}`, elements });
168
+ elements = [];
169
+ y = 5;
170
+ pageNum++;
171
+ }
172
+ elements.push({
173
+ type: "text",
174
+ content: code,
175
+ position: { x: 0, y },
176
+ width: contentWidth,
177
+ style: { fontFamily: "JetBrains Mono", fontSize: 9, color: "#1e293b", backgroundColor: "#f1f5f9", padding: 8, borderRadius: 4, lineHeight: 1.5 }
178
+ });
179
+ y += height2;
180
+ continue;
181
+ }
182
+ if (line.trim() === "") {
183
+ y += 3;
184
+ i++;
185
+ continue;
186
+ }
187
+ let para = "";
188
+ while (i < lines.length && lines[i].trim() !== "" && !lines[i].match(/^#{1,6}\s/) && !lines[i].match(/^\s*[-*+]\s/) && !lines[i].startsWith("```")) {
189
+ para += (para ? " " : "") + lines[i].trim();
190
+ i++;
191
+ }
192
+ const paraLines = Math.ceil(para.length / 80);
193
+ const height = paraLines * 4.5 + 3;
194
+ if (y + height > maxY) {
195
+ pages.push({ id: `page-${pageNum}`, elements });
196
+ elements = [];
197
+ y = 5;
198
+ pageNum++;
199
+ }
200
+ elements.push({
201
+ type: "text",
202
+ content: para,
203
+ position: { x: 0, y },
204
+ width: contentWidth,
205
+ style: { fontFamily: "Inter", fontSize: 10, lineHeight: 1.6, color: "#334155" }
206
+ });
207
+ y += height;
208
+ }
209
+ if (elements.length > 0) {
210
+ pages.push({ id: `page-${pageNum}`, elements });
211
+ }
212
+ return {
213
+ $jdf: "1.0.0",
214
+ meta: {
215
+ title,
216
+ pageSize: "A4",
217
+ unit: "mm",
218
+ margins: { top: 25, right: 22, bottom: 25, left: 22 }
219
+ },
220
+ styles: {
221
+ heading: { fontFamily: "Inter", fontSize: 22, fontWeight: "bold", color: "#0f172a" },
222
+ body: { fontFamily: "Inter", fontSize: 10, lineHeight: 1.6, color: "#334155" }
223
+ },
224
+ pages
225
+ };
226
+ }
227
+
228
+ // src/commands/import-pdf.ts
229
+ async function importPdfPlaceholder(_inputPath, _outputPath) {
230
+ console.error("PDF import via CLI is not yet wired up \u2014 run the JDF Reader app and use Open / drag-drop, which uses the Rust pdf-extract pipeline.");
231
+ console.error("Track progress at: https://github.com/uurtech/jdf");
232
+ process.exit(2);
233
+ }
234
+
235
+ // src/index.ts
236
+ var HELP = `jdf \u2014 JSON Document Format CLI
237
+
238
+ Usage:
239
+ jdf validate <file.jdf>
240
+ jdf import <file.{md,pdf}> [-o output.jdf]
241
+ jdf --help
242
+
243
+ Commands:
244
+ validate Validate a .jdf file against the JDF schema
245
+ import Convert a markdown or PDF file to JDF
246
+
247
+ Examples:
248
+ jdf validate spec/examples/hello-world.jdf
249
+ jdf import README.md
250
+ jdf import paper.pdf -o paper.jdf
251
+ `;
252
+ function parseArgs(argv) {
253
+ const positional = [];
254
+ const flags = {};
255
+ let command;
256
+ for (let i = 0; i < argv.length; i++) {
257
+ const a = argv[i];
258
+ if (i === 0 && !a.startsWith("-")) {
259
+ command = a;
260
+ continue;
261
+ }
262
+ if (a === "--help" || a === "-h") {
263
+ flags["help"] = true;
264
+ continue;
265
+ }
266
+ if (a.startsWith("--")) {
267
+ const k = a.slice(2);
268
+ const next = argv[i + 1];
269
+ if (next && !next.startsWith("-")) {
270
+ flags[k] = next;
271
+ i++;
272
+ } else flags[k] = true;
273
+ continue;
274
+ }
275
+ if (a === "-o" || a === "--output") {
276
+ const next = argv[i + 1];
277
+ if (next) {
278
+ flags["output"] = next;
279
+ i++;
280
+ }
281
+ continue;
282
+ }
283
+ positional.push(a);
284
+ }
285
+ return { command, positional, flags };
286
+ }
287
+ async function main() {
288
+ const { command, positional, flags } = parseArgs(process.argv.slice(2));
289
+ if (!command || flags.help) {
290
+ console.log(HELP);
291
+ process.exit(command ? 0 : 1);
292
+ }
293
+ try {
294
+ switch (command) {
295
+ case "validate": {
296
+ if (!positional[0]) {
297
+ console.error("Usage: jdf validate <file.jdf>");
298
+ process.exit(1);
299
+ }
300
+ const ok = await validate(positional[0]);
301
+ process.exit(ok ? 0 : 1);
302
+ }
303
+ case "import": {
304
+ const input = positional[0];
305
+ if (!input) {
306
+ console.error("Usage: jdf import <file.{md,pdf}> [-o output.jdf]");
307
+ process.exit(1);
308
+ }
309
+ const output = typeof flags.output === "string" ? flags.output : void 0;
310
+ const lower = input.toLowerCase();
311
+ if (lower.endsWith(".md") || lower.endsWith(".markdown")) {
312
+ await importMarkdown(input, output);
313
+ } else if (lower.endsWith(".pdf")) {
314
+ await importPdfPlaceholder(input, output);
315
+ } else {
316
+ console.error(`Unsupported file type: ${input}`);
317
+ process.exit(1);
318
+ }
319
+ break;
320
+ }
321
+ default:
322
+ console.error(`Unknown command: ${command}`);
323
+ console.log(HELP);
324
+ process.exit(1);
325
+ }
326
+ } catch (e) {
327
+ console.error(`Error: ${e.message || e}`);
328
+ process.exit(1);
329
+ }
330
+ }
331
+ main();
@@ -0,0 +1,298 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://jdf.dev/schema/1.0.0/jdf-schema.json",
4
+ "title": "JDF Document",
5
+ "description": "JSON Document Format — a human-readable alternative to PDF",
6
+ "type": "object",
7
+ "required": ["$jdf", "meta", "pages"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "$jdf": {
11
+ "type": "string",
12
+ "description": "JDF format version (semver)",
13
+ "pattern": "^\\d+\\.\\d+\\.\\d+$",
14
+ "examples": ["1.0.0"]
15
+ },
16
+ "meta": { "$ref": "#/definitions/Meta" },
17
+ "styles": {
18
+ "type": "object",
19
+ "description": "Named reusable style definitions",
20
+ "additionalProperties": { "$ref": "#/definitions/Style" }
21
+ },
22
+ "resources": { "$ref": "#/definitions/Resources" },
23
+ "header": { "$ref": "#/definitions/HeaderFooter" },
24
+ "footer": { "$ref": "#/definitions/HeaderFooter" },
25
+ "pages": {
26
+ "type": "array",
27
+ "minItems": 1,
28
+ "items": { "$ref": "#/definitions/Page" }
29
+ }
30
+ },
31
+ "definitions": {
32
+ "Meta": {
33
+ "type": "object",
34
+ "required": ["title"],
35
+ "properties": {
36
+ "title": { "type": "string" },
37
+ "author": { "type": "string" },
38
+ "created": { "type": "string", "format": "date-time" },
39
+ "modified": { "type": "string", "format": "date-time" },
40
+ "language": { "type": "string" },
41
+ "keywords": { "type": "array", "items": { "type": "string" } },
42
+ "pageSize": { "$ref": "#/definitions/PageSize" },
43
+ "pageOrientation": { "$ref": "#/definitions/PageOrientation" },
44
+ "margins": { "$ref": "#/definitions/Margins" },
45
+ "unit": { "$ref": "#/definitions/Unit" }
46
+ }
47
+ },
48
+ "PageSize": {
49
+ "oneOf": [
50
+ { "type": "string", "enum": ["A4", "A3", "A5", "Letter", "Legal", "Tabloid"] },
51
+ { "type": "object", "required": ["width", "height"], "properties": { "width": { "type": "number" }, "height": { "type": "number" } } }
52
+ ]
53
+ },
54
+ "PageOrientation": { "type": "string", "enum": ["portrait", "landscape"] },
55
+ "Unit": { "type": "string", "enum": ["mm", "in", "pt", "px"] },
56
+ "Margins": {
57
+ "type": "object",
58
+ "properties": {
59
+ "top": { "type": "number" }, "right": { "type": "number" },
60
+ "bottom": { "type": "number" }, "left": { "type": "number" }
61
+ }
62
+ },
63
+ "Position": {
64
+ "type": "object",
65
+ "required": ["x", "y"],
66
+ "properties": { "x": { "type": "number" }, "y": { "type": "number" } }
67
+ },
68
+ "Style": {
69
+ "type": "object",
70
+ "properties": {
71
+ "fontFamily": { "type": "string" },
72
+ "fontSize": { "type": "number" },
73
+ "fontWeight": { "oneOf": [ { "type": "string", "enum": ["normal","bold","100","200","300","400","500","600","700","800","900"] }, { "type": "number" } ] },
74
+ "fontStyle": { "type": "string", "enum": ["normal", "italic"] },
75
+ "textDecoration": { "type": "string", "enum": ["none","underline","strikethrough","line-through","underline strikethrough"] },
76
+ "color": { "type": "string" },
77
+ "backgroundColor": { "type": "string" },
78
+ "textAlign": { "type": "string", "enum": ["left","center","right","justify"] },
79
+ "lineHeight": { "type": "number" },
80
+ "letterSpacing": { "oneOf": [{ "type": "number" }, { "type": "string" }] },
81
+ "padding": { "oneOf": [{ "type": "number" }, { "type": "string" }, { "$ref": "#/definitions/Margins" }] },
82
+ "margin": { "oneOf": [{ "type": "number" }, { "type": "string" }, { "$ref": "#/definitions/Margins" }] },
83
+ "marginTop": { "type": "number" },
84
+ "marginBottom": { "type": "number" },
85
+ "border": { "type": "string" },
86
+ "borderRadius": { "oneOf": [{ "type": "number" }, { "type": "string" }] },
87
+ "opacity": { "type": "number", "minimum": 0, "maximum": 1 }
88
+ }
89
+ },
90
+ "StyleRef": {
91
+ "oneOf": [
92
+ { "type": "string" },
93
+ { "type": "array", "items": { "type": "string" } },
94
+ { "$ref": "#/definitions/Style" }
95
+ ]
96
+ },
97
+ "Link": {
98
+ "oneOf": [
99
+ { "type": "string" },
100
+ { "type": "object", "required": ["target"], "properties": { "type": { "type": "string", "enum": ["internal","external"] }, "target": { "type": "string" } } }
101
+ ]
102
+ },
103
+ "Resources": {
104
+ "type": "object",
105
+ "properties": {
106
+ "fonts": { "type": "array", "items": { "$ref": "#/definitions/FontResource" } },
107
+ "images": { "type": "object", "additionalProperties": { "$ref": "#/definitions/ImageResource" } }
108
+ },
109
+ "additionalProperties": { "$ref": "#/definitions/ImageResource" }
110
+ },
111
+ "FontResource": {
112
+ "type": "object",
113
+ "required": ["family", "src"],
114
+ "properties": {
115
+ "family": { "type": "string" },
116
+ "src": { "type": "string", "enum": ["embedded","file","system"] },
117
+ "data": { "type": "string" },
118
+ "path": { "type": "string" },
119
+ "weight": { "type": "string" },
120
+ "style": { "type": "string" }
121
+ }
122
+ },
123
+ "ImageResource": {
124
+ "type": "object",
125
+ "properties": {
126
+ "src": { "type": "string", "enum": ["embedded","file"] },
127
+ "mimeType": { "type": "string" },
128
+ "data": { "type": "string" },
129
+ "path": { "type": "string" }
130
+ }
131
+ },
132
+ "HeaderFooter": {
133
+ "type": "object",
134
+ "properties": {
135
+ "height": { "type": "number" },
136
+ "elements": { "type": "array", "items": { "$ref": "#/definitions/Element" } },
137
+ "content": { "type": "string", "description": "Template string with {{pageNumber}}, {{totalPages}}, {{title}}, {{author}}" },
138
+ "style": { "$ref": "#/definitions/StyleRef" }
139
+ }
140
+ },
141
+ "Page": {
142
+ "type": "object",
143
+ "required": ["elements"],
144
+ "properties": {
145
+ "id": { "type": "string" },
146
+ "pageSize": { "$ref": "#/definitions/PageSize" },
147
+ "pageOrientation": { "$ref": "#/definitions/PageOrientation" },
148
+ "margins": { "$ref": "#/definitions/Margins" },
149
+ "background": { "type": "string" },
150
+ "flow": { "type": "boolean" },
151
+ "header": { "$ref": "#/definitions/HeaderFooter" },
152
+ "footer": { "$ref": "#/definitions/HeaderFooter" },
153
+ "elements": { "type": "array", "items": { "$ref": "#/definitions/Element" } }
154
+ }
155
+ },
156
+ "Element": {
157
+ "oneOf": [
158
+ { "$ref": "#/definitions/TextElement" },
159
+ { "$ref": "#/definitions/RichTextElement" },
160
+ { "$ref": "#/definitions/ImageElement" },
161
+ { "$ref": "#/definitions/TableElement" },
162
+ { "$ref": "#/definitions/ListElement" },
163
+ { "$ref": "#/definitions/ShapeElement" },
164
+ { "$ref": "#/definitions/CollapsibleElement" },
165
+ { "$ref": "#/definitions/TocElement" }
166
+ ]
167
+ },
168
+ "TextElement": {
169
+ "type": "object",
170
+ "required": ["type", "content"],
171
+ "properties": {
172
+ "type": { "const": "text" },
173
+ "content": { "type": "string" },
174
+ "style": { "$ref": "#/definitions/StyleRef" },
175
+ "position": { "$ref": "#/definitions/Position" },
176
+ "width": { "type": "number" },
177
+ "height": { "type": "number" },
178
+ "align": { "type": "string", "enum": ["left","center","right","justify"] },
179
+ "heading": { "oneOf": [{ "type": "boolean" }, { "type": "integer", "minimum": 1, "maximum": 6 }] },
180
+ "tocEntry": { "type": "string" },
181
+ "tocLevel": { "type": "integer", "minimum": 1, "maximum": 6 },
182
+ "link": { "$ref": "#/definitions/Link" }
183
+ }
184
+ },
185
+ "RichTextElement": {
186
+ "type": "object",
187
+ "required": ["type", "runs"],
188
+ "properties": {
189
+ "type": { "const": "richtext" },
190
+ "runs": {
191
+ "type": "array",
192
+ "items": {
193
+ "type": "object",
194
+ "required": ["text"],
195
+ "properties": {
196
+ "text": { "type": "string" },
197
+ "bold": { "type": "boolean" }, "italic": { "type": "boolean" },
198
+ "underline": { "type": "boolean" }, "strikethrough": { "type": "boolean" },
199
+ "color": { "type": "string" }, "fontSize": { "type": "number" }, "fontFamily": { "type": "string" },
200
+ "style": { "$ref": "#/definitions/StyleRef" }, "link": { "$ref": "#/definitions/Link" }
201
+ }
202
+ }
203
+ },
204
+ "style": { "$ref": "#/definitions/StyleRef" },
205
+ "position": { "$ref": "#/definitions/Position" },
206
+ "width": { "type": "number" }, "height": { "type": "number" }
207
+ }
208
+ },
209
+ "ImageElement": {
210
+ "type": "object",
211
+ "required": ["type"],
212
+ "anyOf": [{ "required": ["resource"] }, { "required": ["src"] }],
213
+ "properties": {
214
+ "type": { "const": "image" },
215
+ "resource": { "type": "string" }, "src": { "type": "string" }, "alt": { "type": "string" },
216
+ "position": { "$ref": "#/definitions/Position" },
217
+ "width": { "type": "number" }, "height": { "type": "number" },
218
+ "fit": { "type": "string", "enum": ["contain","cover","fill","none"] },
219
+ "link": { "$ref": "#/definitions/Link" },
220
+ "style": { "$ref": "#/definitions/StyleRef" }
221
+ }
222
+ },
223
+ "TableElement": {
224
+ "type": "object",
225
+ "required": ["type", "rows"],
226
+ "properties": {
227
+ "type": { "const": "table" },
228
+ "columns": {
229
+ "type": "array",
230
+ "items": { "type": "object", "properties": { "width": { "oneOf": [{ "type": "string" }, { "type": "number" }] }, "header": { "type": "string" }, "align": { "type": "string", "enum": ["left","center","right","justify"] } } }
231
+ },
232
+ "headers": { "type": "array", "items": { "type": "string" } },
233
+ "rows": {
234
+ "type": "array",
235
+ "items": { "type": "array", "items": { "oneOf": [{ "type": "string" }, { "type": "object", "required": ["content"], "properties": { "content": { "type": "string" }, "style": { "$ref": "#/definitions/StyleRef" }, "colspan": { "type": "integer" }, "rowspan": { "type": "integer" } } }] } }
236
+ },
237
+ "position": { "$ref": "#/definitions/Position" }, "width": { "type": "number" },
238
+ "headerStyle": { "$ref": "#/definitions/StyleRef" },
239
+ "rowStyle": { "$ref": "#/definitions/StyleRef" },
240
+ "alternateRowStyle": { "$ref": "#/definitions/StyleRef" },
241
+ "alternatingRowColor": { "type": "string" },
242
+ "borders": { "oneOf": [{ "type": "boolean" }, { "type": "object", "properties": { "outer": { "type": "boolean" }, "inner": { "type": "boolean" }, "color": { "type": "string" }, "width": { "type": "number" } } }] },
243
+ "style": { "$ref": "#/definitions/StyleRef" }
244
+ }
245
+ },
246
+ "ListElement": {
247
+ "type": "object",
248
+ "required": ["type", "items"],
249
+ "properties": {
250
+ "type": { "const": "list" },
251
+ "items": {
252
+ "type": "array",
253
+ "items": { "type": "object", "required": ["content"], "properties": { "content": { "type": "string" }, "style": { "$ref": "#/definitions/StyleRef" }, "children": { "type": "array", "items": { "$ref": "#/definitions/ListElement/properties/items/items" } }, "listType": { "type": "string", "enum": ["ordered","unordered"] } } }
254
+ },
255
+ "listType": { "type": "string", "enum": ["ordered","unordered"] },
256
+ "ordered": { "type": "boolean" },
257
+ "position": { "$ref": "#/definitions/Position" }, "width": { "type": "number" },
258
+ "style": { "$ref": "#/definitions/StyleRef" }
259
+ }
260
+ },
261
+ "ShapeElement": {
262
+ "type": "object",
263
+ "required": ["type", "shape"],
264
+ "properties": {
265
+ "type": { "const": "shape" },
266
+ "shape": { "type": "string", "enum": ["rect","circle","ellipse","line","path"] },
267
+ "position": { "$ref": "#/definitions/Position" }, "width": { "type": "number" }, "height": { "type": "number" },
268
+ "fill": { "type": "string" },
269
+ "stroke": { "oneOf": [{ "type": "string" }, { "type": "object", "properties": { "color": { "type": "string" }, "width": { "type": "number" } } }] },
270
+ "strokeWidth": { "type": "number" }, "borderRadius": { "type": "number" }, "path": { "type": "string" },
271
+ "points": { "type": "array", "items": { "$ref": "#/definitions/Position" } },
272
+ "style": { "$ref": "#/definitions/StyleRef" }
273
+ }
274
+ },
275
+ "CollapsibleElement": {
276
+ "type": "object",
277
+ "required": ["type", "title", "elements"],
278
+ "properties": {
279
+ "type": { "const": "collapsible" },
280
+ "title": { "type": "string" },
281
+ "elements": { "type": "array", "items": { "$ref": "#/definitions/Element" } },
282
+ "expanded": { "type": "boolean" },
283
+ "position": { "$ref": "#/definitions/Position" }, "width": { "type": "number" },
284
+ "style": { "$ref": "#/definitions/StyleRef" }
285
+ }
286
+ },
287
+ "TocElement": {
288
+ "type": "object",
289
+ "required": ["type"],
290
+ "properties": {
291
+ "type": { "const": "toc" }, "title": { "type": "string" },
292
+ "position": { "$ref": "#/definitions/Position" }, "width": { "type": "number" },
293
+ "depth": { "type": "integer", "minimum": 1, "maximum": 6 },
294
+ "style": { "$ref": "#/definitions/StyleRef" }
295
+ }
296
+ }
297
+ }
298
+ }
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@uurtech/jdf-cli",
3
+ "version": "0.1.1",
4
+ "description": "Command-line tool for the JDF (JSON Document Format) — validate and convert documents.",
5
+ "license": "MIT",
6
+ "author": "Ugur Kazdal",
7
+ "homepage": "https://github.com/uurtech/jdf#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/uurtech/jdf.git",
11
+ "directory": "tools/jdf-cli"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/uurtech/jdf/issues"
15
+ },
16
+ "keywords": [
17
+ "jdf",
18
+ "json",
19
+ "document",
20
+ "cli",
21
+ "validator",
22
+ "ajv",
23
+ "json-schema",
24
+ "pdf-alternative"
25
+ ],
26
+ "type": "module",
27
+ "main": "./dist/index.js",
28
+ "bin": {
29
+ "jdf": "./dist/index.js"
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "README.md",
34
+ "LICENSE"
35
+ ],
36
+ "engines": {
37
+ "node": ">=18"
38
+ },
39
+ "scripts": {
40
+ "build": "tsup",
41
+ "dev": "tsup --watch",
42
+ "start": "tsx src/index.ts",
43
+ "typecheck": "tsc --noEmit",
44
+ "prepublishOnly": "pnpm build"
45
+ },
46
+ "dependencies": {
47
+ "ajv": "^8.17.1",
48
+ "ajv-formats": "^3.0.1"
49
+ },
50
+ "devDependencies": {
51
+ "@jdf/core": "workspace:*",
52
+ "@types/node": "^22.10.0",
53
+ "tsup": "^8.3.5",
54
+ "tsx": "^4.19.2",
55
+ "typescript": "^5.5.0"
56
+ }
57
+ }