@forwardimpact/libsyntheticrender 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/LICENSE +201 -0
- package/format.js +84 -0
- package/index.js +7 -0
- package/package.json +37 -0
- package/render/dataset-renderers.js +187 -0
- package/render/enricher.js +384 -0
- package/render/html.js +458 -0
- package/render/industry-data.js +434 -0
- package/render/link-assigner.js +350 -0
- package/render/markdown.js +126 -0
- package/render/pathway.js +124 -0
- package/render/raw.js +465 -0
- package/render/renderer.js +122 -0
- package/render/validate-links.js +329 -0
- package/templates/article.html +31 -0
- package/templates/blog-post.html +34 -0
- package/templates/blog.html +27 -0
- package/templates/briefing.md +20 -0
- package/templates/comments.html +15 -0
- package/templates/courses.html +37 -0
- package/templates/departments.html +29 -0
- package/templates/drugs.html +23 -0
- package/templates/events.html +38 -0
- package/templates/faq.html +22 -0
- package/templates/howto.html +8 -0
- package/templates/leadership.html +8 -0
- package/templates/ontology.md +34 -0
- package/templates/page.html +12 -0
- package/templates/platforms.html +23 -0
- package/templates/project-note.md +19 -0
- package/templates/projects.html +42 -0
- package/templates/readme.md +28 -0
- package/templates/reviews.html +19 -0
- package/templates/roles.html +11 -0
- package/templates/skill-reflection.md +18 -0
- package/templates/weekly.md +18 -0
- package/test/dataset-renderers.test.js +214 -0
- package/test/validate.test.js +396 -0
- package/validate.js +535 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{{#platforms}}
|
|
2
|
+
<div
|
|
3
|
+
itemscope
|
|
4
|
+
itemtype="https://schema.org/SoftwareApplication"
|
|
5
|
+
itemid="{{{iri}}}"
|
|
6
|
+
>
|
|
7
|
+
<h2 itemprop="name">{{name}}</h2>
|
|
8
|
+
<meta itemprop="applicationCategory" content="{{category}}" />
|
|
9
|
+
<meta itemprop="softwareVersion" content="{{version}}" />
|
|
10
|
+
|
|
11
|
+
{{#dependencies}}
|
|
12
|
+
<link itemprop="softwareRequirements" href="{{{iri}}}" />
|
|
13
|
+
{{/dependencies}} {{#projectLinks}}
|
|
14
|
+
<link itemprop="isRelatedTo" href="{{{iri}}}" />
|
|
15
|
+
{{/projectLinks}} {{#drugLinks}}
|
|
16
|
+
<link itemprop="isRelatedTo" href="{{{iri}}}" />
|
|
17
|
+
{{/drugLinks}}
|
|
18
|
+
|
|
19
|
+
<div itemprop="description" data-enrich="platform_{{id}}">
|
|
20
|
+
{{description}}
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
{{/platforms}}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: project-note
|
|
3
|
+
person: {{personId}}
|
|
4
|
+
project: {{projectId}}
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# {{projectName}} — Notes
|
|
8
|
+
|
|
9
|
+
**Author:** {{personName}} **Project Type:** {{projectType}} **Timeline:**
|
|
10
|
+
{{timeline_start}} to {{timeline_end}}
|
|
11
|
+
|
|
12
|
+
## Notes
|
|
13
|
+
|
|
14
|
+
{{note}}
|
|
15
|
+
|
|
16
|
+
## Key Dates
|
|
17
|
+
|
|
18
|
+
- Start: {{timeline_start}}
|
|
19
|
+
- End: {{timeline_end}}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{{#projects}}
|
|
2
|
+
<article itemscope itemtype="https://schema.org/Project" itemid="{{{iri}}}">
|
|
3
|
+
<h2 itemprop="name">{{name}}</h2>
|
|
4
|
+
<meta itemprop="identifier" content="{{id}}" />
|
|
5
|
+
{{#timeline_start}}<meta
|
|
6
|
+
itemprop="startDate"
|
|
7
|
+
content="{{timeline_start}}"
|
|
8
|
+
/>{{/timeline_start}} {{#timeline_end}}<meta
|
|
9
|
+
itemprop="endDate"
|
|
10
|
+
content="{{timeline_end}}"
|
|
11
|
+
/>{{/timeline_end}} {{#leader}}
|
|
12
|
+
<div
|
|
13
|
+
itemprop="creator"
|
|
14
|
+
itemscope
|
|
15
|
+
itemtype="https://schema.org/Person"
|
|
16
|
+
itemid="{{{iri}}}"
|
|
17
|
+
>
|
|
18
|
+
<span itemprop="name">{{name}}</span>
|
|
19
|
+
<meta itemprop="email" content="{{email}}" />
|
|
20
|
+
</div>
|
|
21
|
+
{{/leader}} {{#members}}
|
|
22
|
+
<div
|
|
23
|
+
itemprop="contributor"
|
|
24
|
+
itemscope
|
|
25
|
+
itemtype="https://schema.org/Person"
|
|
26
|
+
itemid="{{{iri}}}"
|
|
27
|
+
>
|
|
28
|
+
<span itemprop="name">{{name}}</span>
|
|
29
|
+
</div>
|
|
30
|
+
{{/members}} {{#drugLinks}}
|
|
31
|
+
<link itemprop="about" href="{{{iri}}}" />
|
|
32
|
+
{{/drugLinks}} {{#platformLinks}}
|
|
33
|
+
<link itemprop="isPartOf" href="{{{iri}}}" />
|
|
34
|
+
{{/platformLinks}} {{#departmentLinks}}
|
|
35
|
+
<link itemprop="isPartOf" href="{{{iri}}}" />
|
|
36
|
+
{{/departmentLinks}}
|
|
37
|
+
|
|
38
|
+
<div itemprop="description" data-enrich="project_{{id}}">
|
|
39
|
+
{{name}} is a {{type}} project{{#phase}} currently in {{phase}}{{/phase}}.
|
|
40
|
+
</div>
|
|
41
|
+
</article>
|
|
42
|
+
{{/projects}}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# {{orgName}}
|
|
2
|
+
|
|
3
|
+
{{overview}}
|
|
4
|
+
|
|
5
|
+
## Departments
|
|
6
|
+
|
|
7
|
+
{{#departments}}
|
|
8
|
+
|
|
9
|
+
### {{{name}}}
|
|
10
|
+
|
|
11
|
+
{{#teams}}
|
|
12
|
+
|
|
13
|
+
- **{{{name}}}** ({{size}} members) {{/teams}}
|
|
14
|
+
|
|
15
|
+
{{/departments}}
|
|
16
|
+
|
|
17
|
+
## Projects
|
|
18
|
+
|
|
19
|
+
{{#projects}}
|
|
20
|
+
|
|
21
|
+
### {{{name}}}
|
|
22
|
+
|
|
23
|
+
- Type: {{type}}
|
|
24
|
+
- Timeline: {{timeline_start}} to {{timeline_end}}
|
|
25
|
+
|
|
26
|
+
{{prose}}
|
|
27
|
+
|
|
28
|
+
{{/projects}}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{{#reviews}}
|
|
2
|
+
<div itemscope itemtype="https://schema.org/Review" itemid="{{{iri}}}">
|
|
3
|
+
<span
|
|
4
|
+
itemprop="author"
|
|
5
|
+
itemscope
|
|
6
|
+
itemtype="https://schema.org/Person"
|
|
7
|
+
itemid="{{{authorIri}}}"
|
|
8
|
+
>
|
|
9
|
+
<span itemprop="name">{{author}}</span>
|
|
10
|
+
</span>
|
|
11
|
+
<span itemprop="reviewBody">{{body}}</span>
|
|
12
|
+
<div itemprop="reviewRating" itemscope itemtype="https://schema.org/Rating">
|
|
13
|
+
<meta itemprop="ratingValue" content="{{rating}}" />
|
|
14
|
+
<meta itemprop="bestRating" content="5" />
|
|
15
|
+
<meta itemprop="worstRating" content="1" />
|
|
16
|
+
</div>
|
|
17
|
+
<link itemprop="itemReviewed" href="{{{reviewedIri}}}" />
|
|
18
|
+
</div>
|
|
19
|
+
{{/reviews}}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{{#levels}}
|
|
2
|
+
<div
|
|
3
|
+
itemscope
|
|
4
|
+
itemtype="https://schema.org/Role"
|
|
5
|
+
itemid="{{{domain}}}/role/{{id}}"
|
|
6
|
+
>
|
|
7
|
+
<span itemprop="name">{{id}}</span>
|
|
8
|
+
<meta itemprop="occupationalCategory" content="Engineering" />
|
|
9
|
+
<span>{{count}} people at this level</span>
|
|
10
|
+
</div>
|
|
11
|
+
{{/levels}}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: skill-reflection
|
|
3
|
+
person: {{personId}}
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill Reflections — {{personName}}
|
|
7
|
+
|
|
8
|
+
**Discipline:** {{discipline}} **Level:** {{level}}
|
|
9
|
+
|
|
10
|
+
## Current Proficiency Areas
|
|
11
|
+
|
|
12
|
+
{{#skills}}
|
|
13
|
+
|
|
14
|
+
- **{{label}}:** {{proficiency}} {{/skills}}
|
|
15
|
+
|
|
16
|
+
## Growth Areas
|
|
17
|
+
|
|
18
|
+
{{growthNote}}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: weekly
|
|
3
|
+
person: {{personId}}
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Weekly Notes — {{personName}}
|
|
7
|
+
|
|
8
|
+
**Week of:** {{date}} **Team:** {{teamName}}
|
|
9
|
+
|
|
10
|
+
## Reflections
|
|
11
|
+
|
|
12
|
+
{{weeklyNote}}
|
|
13
|
+
|
|
14
|
+
## Action Items
|
|
15
|
+
|
|
16
|
+
- [ ] Review team priorities
|
|
17
|
+
- [ ] Update skill assessments
|
|
18
|
+
- [ ] Check project milestones
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { describe, test } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { renderDataset } from "../render/dataset-renderers.js";
|
|
4
|
+
|
|
5
|
+
const FIXTURE = {
|
|
6
|
+
name: "test_records",
|
|
7
|
+
schema: null,
|
|
8
|
+
records: [
|
|
9
|
+
{ id: 1, name: "Alice", active: true, score: 9.5, tags: null },
|
|
10
|
+
{ id: 2, name: "Bob", active: false, score: 7.2, tags: ["a", "b"] },
|
|
11
|
+
{ id: 3, name: "Carol", active: true, score: 8.0, tags: { nested: true } },
|
|
12
|
+
],
|
|
13
|
+
metadata: { tool: "test" },
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const EMPTY = {
|
|
17
|
+
name: "empty",
|
|
18
|
+
schema: null,
|
|
19
|
+
records: [],
|
|
20
|
+
metadata: { tool: "test" },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
describe("dataset renderers", () => {
|
|
24
|
+
describe("JSON", () => {
|
|
25
|
+
test("renders valid JSON", async () => {
|
|
26
|
+
const result = await renderDataset(FIXTURE, "json", {
|
|
27
|
+
path: "out/test.json",
|
|
28
|
+
});
|
|
29
|
+
assert.strictEqual(result.size, 1);
|
|
30
|
+
const content = result.get("out/test.json");
|
|
31
|
+
const parsed = JSON.parse(content);
|
|
32
|
+
assert.strictEqual(parsed.length, 3);
|
|
33
|
+
assert.strictEqual(parsed[0].name, "Alice");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("round-trips records", async () => {
|
|
37
|
+
const result = await renderDataset(FIXTURE, "json", { path: "x.json" });
|
|
38
|
+
const parsed = JSON.parse(result.get("x.json"));
|
|
39
|
+
assert.deepStrictEqual(parsed, FIXTURE.records);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("handles empty dataset", async () => {
|
|
43
|
+
const result = await renderDataset(EMPTY, "json", { path: "e.json" });
|
|
44
|
+
assert.deepStrictEqual(JSON.parse(result.get("e.json")), []);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("YAML", () => {
|
|
49
|
+
test("renders YAML content", async () => {
|
|
50
|
+
const result = await renderDataset(FIXTURE, "yaml", {
|
|
51
|
+
path: "out/test.yaml",
|
|
52
|
+
});
|
|
53
|
+
const content = result.get("out/test.yaml");
|
|
54
|
+
assert.ok(content.includes("Alice"));
|
|
55
|
+
assert.ok(content.includes("Bob"));
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("CSV", () => {
|
|
60
|
+
test("renders header and rows", async () => {
|
|
61
|
+
const result = await renderDataset(FIXTURE, "csv", {
|
|
62
|
+
path: "out/test.csv",
|
|
63
|
+
});
|
|
64
|
+
const lines = result.get("out/test.csv").split("\n");
|
|
65
|
+
assert.strictEqual(lines[0], "id,name,active,score,tags");
|
|
66
|
+
assert.ok(lines[1].startsWith("1,Alice,true,9.5,"));
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("quotes values with commas", async () => {
|
|
70
|
+
const ds = {
|
|
71
|
+
name: "comma",
|
|
72
|
+
schema: null,
|
|
73
|
+
records: [{ val: "hello, world" }],
|
|
74
|
+
metadata: {},
|
|
75
|
+
};
|
|
76
|
+
const result = await renderDataset(ds, "csv", { path: "c.csv" });
|
|
77
|
+
const lines = result.get("c.csv").split("\n");
|
|
78
|
+
assert.ok(lines[1].includes('"hello, world"'));
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("handles empty dataset", async () => {
|
|
82
|
+
const result = await renderDataset(EMPTY, "csv", { path: "e.csv" });
|
|
83
|
+
assert.strictEqual(result.get("e.csv"), "");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("serializes nested objects as JSON strings", async () => {
|
|
87
|
+
const ds = {
|
|
88
|
+
name: "nested",
|
|
89
|
+
schema: null,
|
|
90
|
+
records: [{ data: { key: "val" } }],
|
|
91
|
+
metadata: {},
|
|
92
|
+
};
|
|
93
|
+
const result = await renderDataset(ds, "csv", { path: "n.csv" });
|
|
94
|
+
const content = result.get("n.csv");
|
|
95
|
+
// Nested object is serialized as JSON, then CSV-escaped (quotes doubled)
|
|
96
|
+
assert.ok(content.includes('{""key"":""val""}'));
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe("Markdown", () => {
|
|
101
|
+
test("renders table with header", async () => {
|
|
102
|
+
const result = await renderDataset(FIXTURE, "markdown", {
|
|
103
|
+
path: "out/test.md",
|
|
104
|
+
});
|
|
105
|
+
const content = result.get("out/test.md");
|
|
106
|
+
assert.ok(content.startsWith("# test_records"));
|
|
107
|
+
assert.ok(content.includes("| id | name |"));
|
|
108
|
+
assert.ok(content.includes("| --- |"));
|
|
109
|
+
assert.ok(content.includes("Alice"));
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("handles empty dataset", async () => {
|
|
113
|
+
const result = await renderDataset(EMPTY, "markdown", { path: "e.md" });
|
|
114
|
+
const content = result.get("e.md");
|
|
115
|
+
assert.ok(content.includes("No records."));
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("escapes pipe characters", async () => {
|
|
119
|
+
const ds = {
|
|
120
|
+
name: "pipe",
|
|
121
|
+
schema: null,
|
|
122
|
+
records: [{ val: "a|b" }],
|
|
123
|
+
metadata: {},
|
|
124
|
+
};
|
|
125
|
+
const result = await renderDataset(ds, "markdown", { path: "p.md" });
|
|
126
|
+
assert.ok(result.get("p.md").includes("a\\|b"));
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe("SQL INSERT", () => {
|
|
131
|
+
test("renders INSERT statement", async () => {
|
|
132
|
+
const result = await renderDataset(FIXTURE, "sql", {
|
|
133
|
+
path: "out/test.sql",
|
|
134
|
+
table: "my_table",
|
|
135
|
+
});
|
|
136
|
+
const content = result.get("out/test.sql");
|
|
137
|
+
assert.ok(content.startsWith('INSERT INTO "my_table"'));
|
|
138
|
+
assert.ok(content.includes('"id"'));
|
|
139
|
+
assert.ok(content.includes("'Alice'"));
|
|
140
|
+
assert.ok(content.includes("TRUE"));
|
|
141
|
+
assert.ok(content.includes("FALSE"));
|
|
142
|
+
assert.ok(content.endsWith(";\n"));
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("uses dataset name as default table", async () => {
|
|
146
|
+
const result = await renderDataset(FIXTURE, "sql", {
|
|
147
|
+
path: "out/test.sql",
|
|
148
|
+
});
|
|
149
|
+
const content = result.get("out/test.sql");
|
|
150
|
+
assert.ok(content.includes('"test_records"'));
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("escapes single quotes in strings", async () => {
|
|
154
|
+
const ds = {
|
|
155
|
+
name: "esc",
|
|
156
|
+
schema: null,
|
|
157
|
+
records: [{ val: "it's" }],
|
|
158
|
+
metadata: {},
|
|
159
|
+
};
|
|
160
|
+
const result = await renderDataset(ds, "sql", {
|
|
161
|
+
path: "e.sql",
|
|
162
|
+
table: "t",
|
|
163
|
+
});
|
|
164
|
+
assert.ok(result.get("e.sql").includes("'it''s'"));
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("renders NULL for null values", async () => {
|
|
168
|
+
const result = await renderDataset(FIXTURE, "sql", {
|
|
169
|
+
path: "n.sql",
|
|
170
|
+
table: "t",
|
|
171
|
+
});
|
|
172
|
+
assert.ok(result.get("n.sql").includes("NULL"));
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("handles empty dataset", async () => {
|
|
176
|
+
const result = await renderDataset(EMPTY, "sql", {
|
|
177
|
+
path: "e.sql",
|
|
178
|
+
table: "t",
|
|
179
|
+
});
|
|
180
|
+
assert.ok(result.get("e.sql").includes("No records"));
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe("Parquet", () => {
|
|
185
|
+
test("renders parquet buffer", async () => {
|
|
186
|
+
const simpleDs = {
|
|
187
|
+
name: "parq",
|
|
188
|
+
schema: null,
|
|
189
|
+
records: [
|
|
190
|
+
{ id: 1, name: "Alice" },
|
|
191
|
+
{ id: 2, name: "Bob" },
|
|
192
|
+
],
|
|
193
|
+
metadata: {},
|
|
194
|
+
};
|
|
195
|
+
const result = await renderDataset(simpleDs, "parquet", {
|
|
196
|
+
path: "out/test.parquet",
|
|
197
|
+
});
|
|
198
|
+
const buf = result.get("out/test.parquet");
|
|
199
|
+
assert.ok(Buffer.isBuffer(buf));
|
|
200
|
+
assert.ok(buf.length > 0);
|
|
201
|
+
// Parquet magic bytes: PAR1
|
|
202
|
+
assert.strictEqual(buf.toString("ascii", 0, 4), "PAR1");
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe("dispatch", () => {
|
|
207
|
+
test("throws on unknown format", async () => {
|
|
208
|
+
await assert.rejects(
|
|
209
|
+
() => renderDataset(FIXTURE, "xlsx", { path: "x" }),
|
|
210
|
+
/Unknown format: xlsx/,
|
|
211
|
+
);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
});
|