@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.
@@ -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
+ });