@forwardimpact/pathway 0.25.20 → 0.25.21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forwardimpact/pathway",
3
- "version": "0.25.20",
3
+ "version": "0.25.21",
4
4
  "description": "Career progression web app and CLI for exploring roles and generating agent teams",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -7,7 +7,6 @@
7
7
 
8
8
  // Shared utilities
9
9
  export * from "./shared.js";
10
- export * from "./microdata-shared.js";
11
10
 
12
11
  // Job formatters
13
12
  export { jobToMarkdown } from "./job/markdown.js";
@@ -23,15 +22,10 @@ export { progressToDOM } from "./progress/dom.js";
23
22
 
24
23
  // Driver formatters
25
24
  export { driverToDOM } from "./driver/dom.js";
26
- export {
27
- driverListToMicrodata,
28
- driverToMicrodata,
29
- } from "./driver/microdata.js";
30
25
 
31
26
  // Skill formatters
32
27
  export { skillListToMarkdown, skillToMarkdown } from "./skill/markdown.js";
33
28
  export { skillToDOM } from "./skill/dom.js";
34
- export { skillListToMicrodata, skillToMicrodata } from "./skill/microdata.js";
35
29
 
36
30
  // Behaviour formatters
37
31
  export {
@@ -39,10 +33,6 @@ export {
39
33
  behaviourToMarkdown,
40
34
  } from "./behaviour/markdown.js";
41
35
  export { behaviourToDOM } from "./behaviour/dom.js";
42
- export {
43
- behaviourListToMicrodata,
44
- behaviourToMicrodata,
45
- } from "./behaviour/microdata.js";
46
36
 
47
37
  // Discipline formatters
48
38
  export {
@@ -50,23 +40,14 @@ export {
50
40
  disciplineToMarkdown,
51
41
  } from "./discipline/markdown.js";
52
42
  export { disciplineToDOM } from "./discipline/dom.js";
53
- export {
54
- disciplineListToMicrodata,
55
- disciplineToMicrodata,
56
- } from "./discipline/microdata.js";
57
43
 
58
44
  // Level formatters
59
45
  export { levelListToMarkdown, levelToMarkdown } from "./level/markdown.js";
60
46
  export { levelToDOM } from "./level/dom.js";
61
- export { levelListToMicrodata, levelToMicrodata } from "./level/microdata.js";
62
47
 
63
48
  // Track formatters
64
49
  export { trackListToMarkdown, trackToMarkdown } from "./track/markdown.js";
65
50
  export { trackToDOM } from "./track/dom.js";
66
- export { trackListToMicrodata, trackToMicrodata } from "./track/microdata.js";
67
-
68
- // Stage formatters
69
- export { stageListToMicrodata, stageToMicrodata } from "./stage/microdata.js";
70
51
 
71
52
  // JSON-LD formatters
72
53
  export {
@@ -1,106 +0,0 @@
1
- /**
2
- * Behaviour formatting for microdata HTML output
3
- *
4
- * Generates clean, class-less HTML with microdata aligned with behaviour.schema.json
5
- * RDF vocab: https://www.forwardimpact.team/schema/rdf/
6
- */
7
-
8
- import {
9
- openTag,
10
- prop,
11
- propRaw,
12
- metaTag,
13
- section,
14
- dl,
15
- ul,
16
- escapeHtml,
17
- formatLevelName,
18
- htmlDocument,
19
- } from "../microdata-shared.js";
20
- import { prepareBehavioursList, prepareBehaviourDetail } from "./shared.js";
21
-
22
- /**
23
- * Format behaviour list as microdata HTML
24
- * @param {Array} behaviours - Raw behaviour entities
25
- * @returns {string} HTML with microdata
26
- */
27
- export function behaviourListToMicrodata(behaviours) {
28
- const { items } = prepareBehavioursList(behaviours);
29
-
30
- const content = items
31
- .map(
32
- (
33
- behaviour,
34
- ) => `${openTag("article", { itemtype: "Behaviour", itemid: `#${behaviour.id}` })}
35
- ${prop("h2", "name", behaviour.name)}
36
- ${prop("p", "description", behaviour.truncatedDescription)}
37
- </article>`,
38
- )
39
- .join("\n");
40
-
41
- return htmlDocument(
42
- "Behaviours",
43
- `<main>
44
- <h1>Behaviours</h1>
45
- ${content}
46
- </main>`,
47
- );
48
- }
49
-
50
- /**
51
- * Format behaviour detail as microdata HTML
52
- * @param {Object} behaviour - Raw behaviour entity
53
- * @param {Object} context - Additional context
54
- * @param {Array} context.drivers - All drivers
55
- * @returns {string} HTML with microdata
56
- */
57
- export function behaviourToMicrodata(behaviour, { drivers }) {
58
- const view = prepareBehaviourDetail(behaviour, { drivers });
59
-
60
- if (!view) return "";
61
-
62
- const sections = [];
63
-
64
- // Maturity descriptions - uses MaturityDescriptions itemtype
65
- const maturityPairs = Object.entries(view.maturityDescriptions).map(
66
- ([maturity, desc]) => ({
67
- term: formatLevelName(maturity),
68
- definition: desc,
69
- itemprop: `${maturity.replace(/_([a-z])/g, (_, c) => c.toUpperCase())}Description`,
70
- }),
71
- );
72
- sections.push(
73
- section(
74
- "Maturity Levels",
75
- `${openTag("div", { itemtype: "MaturityDescriptions", itemprop: "maturityDescriptions" })}
76
- ${dl(maturityPairs)}
77
- </div>`,
78
- 2,
79
- ),
80
- );
81
-
82
- // Related drivers
83
- if (view.relatedDrivers.length > 0) {
84
- const driverItems = view.relatedDrivers.map(
85
- (d) => `<a href="#${escapeHtml(d.id)}">${escapeHtml(d.name)}</a>`,
86
- );
87
- sections.push(section("Linked to Drivers", ul(driverItems), 2));
88
- }
89
-
90
- const body = `<main>
91
- ${openTag("article", { itemtype: "Behaviour", itemid: `#${view.id}` })}
92
- ${prop("h1", "name", view.name)}
93
- ${metaTag("id", view.id)}
94
- ${propRaw(
95
- "div",
96
- "human",
97
- `${openTag("div", { itemtype: "BehaviourHumanSection" })}
98
- ${prop("p", "description", view.description)}
99
- ${sections.join("\n")}
100
- </div>`,
101
- )}
102
- </article>
103
- </main>`;
104
-
105
- return htmlDocument(view.name, body);
106
- }
@@ -1,117 +0,0 @@
1
- /**
2
- * Discipline formatting for microdata HTML output
3
- *
4
- * Generates clean, class-less HTML with microdata aligned with discipline.schema.json
5
- * RDF vocab: https://www.forwardimpact.team/schema/rdf/
6
- */
7
-
8
- import {
9
- openTag,
10
- prop,
11
- metaTag,
12
- linkTag,
13
- section,
14
- ul,
15
- escapeHtml,
16
- htmlDocument,
17
- } from "../microdata-shared.js";
18
- import { prepareDisciplinesList, prepareDisciplineDetail } from "./shared.js";
19
-
20
- /**
21
- * Format discipline list as microdata HTML
22
- * @param {Array} disciplines - Raw discipline entities
23
- * @returns {string} HTML with microdata
24
- */
25
- export function disciplineListToMicrodata(disciplines) {
26
- const { items } = prepareDisciplinesList(disciplines);
27
-
28
- const content = items
29
- .map(
30
- (
31
- d,
32
- ) => `${openTag("article", { itemtype: "Discipline", itemid: `#${d.id}` })}
33
- ${prop("h2", "specialization", d.name)}
34
- <p>Core: ${d.coreSkillsCount} | Supporting: ${d.supportingSkillsCount} | Broad: ${d.broadSkillsCount}</p>
35
- </article>`,
36
- )
37
- .join("\n");
38
-
39
- return htmlDocument(
40
- "Disciplines",
41
- `<main>
42
- <h1>Disciplines</h1>
43
- ${content}
44
- </main>`,
45
- );
46
- }
47
-
48
- /**
49
- * Format discipline detail as microdata HTML
50
- * @param {Object} discipline - Raw discipline entity
51
- * @param {Object} context - Additional context
52
- * @param {Array} context.skills - All skills
53
- * @param {Array} context.behaviours - All behaviours
54
- * @param {boolean} [context.showBehaviourModifiers=true] - Whether to show behaviour modifiers section
55
- * @returns {string} HTML with microdata
56
- */
57
- export function disciplineToMicrodata(
58
- discipline,
59
- { skills, behaviours, showBehaviourModifiers = true } = {},
60
- ) {
61
- const view = prepareDisciplineDetail(discipline, { skills, behaviours });
62
-
63
- if (!view) return "";
64
-
65
- const sections = [];
66
-
67
- // Core skills - using coreSkills property
68
- if (view.coreSkills.length > 0) {
69
- const skillLinks = view.coreSkills.map(
70
- (s) =>
71
- `${openTag("span", { itemprop: "coreSkills" })}<a href="#${escapeHtml(s.id)}">${escapeHtml(s.name)}</a></span>`,
72
- );
73
- sections.push(section("Core Skills", ul(skillLinks), 2));
74
- }
75
-
76
- // Supporting skills - using supportingSkills property
77
- if (view.supportingSkills.length > 0) {
78
- const skillLinks = view.supportingSkills.map(
79
- (s) =>
80
- `${openTag("span", { itemprop: "supportingSkills" })}<a href="#${escapeHtml(s.id)}">${escapeHtml(s.name)}</a></span>`,
81
- );
82
- sections.push(section("Supporting Skills", ul(skillLinks), 2));
83
- }
84
-
85
- // Broad skills - using broadSkills property
86
- if (view.broadSkills.length > 0) {
87
- const skillLinks = view.broadSkills.map(
88
- (s) =>
89
- `${openTag("span", { itemprop: "broadSkills" })}<a href="#${escapeHtml(s.id)}">${escapeHtml(s.name)}</a></span>`,
90
- );
91
- sections.push(section("Broad Skills", ul(skillLinks), 2));
92
- }
93
-
94
- // Behaviour modifiers - using BehaviourModifier itemtype
95
- if (showBehaviourModifiers && view.behaviourModifiers.length > 0) {
96
- const modifierItems = view.behaviourModifiers.map((b) => {
97
- const modifierStr = b.modifier > 0 ? `+${b.modifier}` : `${b.modifier}`;
98
- return `${openTag("span", { itemtype: "BehaviourModifier", itemprop: "behaviourModifiers" })}
99
- ${linkTag("targetBehaviour", `#${b.id}`)}
100
- <a href="#${escapeHtml(b.id)}">${escapeHtml(b.name)}</a>: ${openTag("span", { itemprop: "modifierValue" })}${modifierStr}</span>
101
- </span>`;
102
- });
103
- sections.push(section("Behaviour Modifiers", ul(modifierItems), 2));
104
- }
105
-
106
- const body = `<main>
107
- ${openTag("article", { itemtype: "Discipline", itemid: `#${view.id}` })}
108
- ${prop("h1", "specialization", view.name)}
109
- ${metaTag("id", view.id)}
110
- ${discipline.roleTitle ? prop("p", "roleTitle", discipline.roleTitle) : ""}
111
- ${prop("p", "description", view.description)}
112
- ${sections.join("\n")}
113
- </article>
114
- </main>`;
115
-
116
- return htmlDocument(view.name, body);
117
- }
@@ -1,91 +0,0 @@
1
- /**
2
- * Driver formatting for microdata HTML output
3
- *
4
- * Generates clean, class-less HTML with microdata aligned with drivers.schema.json
5
- * RDF vocab: https://www.forwardimpact.team/schema/rdf/
6
- */
7
-
8
- import {
9
- openTag,
10
- prop,
11
- metaTag,
12
- section,
13
- ul,
14
- escapeHtml,
15
- htmlDocument,
16
- } from "../microdata-shared.js";
17
- import { prepareDriversList, prepareDriverDetail } from "./shared.js";
18
-
19
- /**
20
- * Format driver list as microdata HTML
21
- * @param {Array} drivers - Raw driver entities
22
- * @returns {string} HTML with microdata
23
- */
24
- export function driverListToMicrodata(drivers) {
25
- const { items } = prepareDriversList(drivers);
26
-
27
- const content = items
28
- .map(
29
- (
30
- driver,
31
- ) => `${openTag("article", { itemtype: "Driver", itemid: `#${driver.id}` })}
32
- ${prop("h2", "name", driver.name)}
33
- ${prop("p", "description", driver.truncatedDescription)}
34
- <p>Skills: ${driver.contributingSkillsCount} | Behaviours: ${driver.contributingBehavioursCount}</p>
35
- </article>`,
36
- )
37
- .join("\n");
38
-
39
- return htmlDocument(
40
- "Drivers",
41
- `<main>
42
- <h1>Drivers</h1>
43
- ${content}
44
- </main>`,
45
- );
46
- }
47
-
48
- /**
49
- * Format driver detail as microdata HTML
50
- * @param {Object} driver - Raw driver entity
51
- * @param {Object} context - Additional context
52
- * @param {Array} context.skills - All skills
53
- * @param {Array} context.behaviours - All behaviours
54
- * @returns {string} HTML with microdata
55
- */
56
- export function driverToMicrodata(driver, { skills, behaviours }) {
57
- const view = prepareDriverDetail(driver, { skills, behaviours });
58
-
59
- if (!view) return "";
60
-
61
- const sections = [];
62
-
63
- // Contributing skills - using contributingSkills property
64
- if (view.contributingSkills.length > 0) {
65
- const skillLinks = view.contributingSkills.map(
66
- (s) =>
67
- `${openTag("span", { itemprop: "contributingSkills" })}<a href="#${escapeHtml(s.id)}">${escapeHtml(s.name)}</a></span>`,
68
- );
69
- sections.push(section("Contributing Skills", ul(skillLinks), 2));
70
- }
71
-
72
- // Contributing behaviours - using contributingBehaviours property
73
- if (view.contributingBehaviours.length > 0) {
74
- const behaviourLinks = view.contributingBehaviours.map(
75
- (b) =>
76
- `${openTag("span", { itemprop: "contributingBehaviours" })}<a href="#${escapeHtml(b.id)}">${escapeHtml(b.name)}</a></span>`,
77
- );
78
- sections.push(section("Contributing Behaviours", ul(behaviourLinks), 2));
79
- }
80
-
81
- const body = `<main>
82
- ${openTag("article", { itemtype: "Driver", itemid: `#${view.id}` })}
83
- ${prop("h1", "name", view.name)}
84
- ${metaTag("id", view.id)}
85
- ${prop("p", "description", view.description)}
86
- ${sections.join("\n")}
87
- </article>
88
- </main>`;
89
-
90
- return htmlDocument(view.name, body);
91
- }
@@ -1,141 +0,0 @@
1
- /**
2
- * Level formatting for microdata HTML output
3
- *
4
- * Generates clean, class-less HTML with microdata aligned with levels.schema.json
5
- * RDF vocab: https://www.forwardimpact.team/schema/rdf/
6
- */
7
-
8
- import {
9
- openTag,
10
- prop,
11
- metaTag,
12
- linkTag,
13
- section,
14
- dl,
15
- escapeHtml,
16
- formatLevelName,
17
- htmlDocument,
18
- } from "../microdata-shared.js";
19
- import { prepareLevelsList, prepareLevelDetail } from "./shared.js";
20
-
21
- /**
22
- * Format level list as microdata HTML
23
- * @param {Array} levels - Raw level entities
24
- * @returns {string} HTML with microdata
25
- */
26
- export function levelListToMicrodata(levels) {
27
- const { items } = prepareLevelsList(levels);
28
-
29
- const content = items
30
- .map(
31
- (g) => `${openTag("article", { itemtype: "Level", itemid: `#${g.id}` })}
32
- ${prop("h2", "id", g.id)}
33
- <p>${escapeHtml(g.displayName)}</p>
34
- ${g.typicalExperienceRange ? prop("p", "typicalExperienceRange", g.typicalExperienceRange) : ""}
35
- ${metaTag("ordinalRank", String(g.ordinalRank))}
36
- </article>`,
37
- )
38
- .join("\n");
39
-
40
- return htmlDocument(
41
- "Levels",
42
- `<main>
43
- <h1>Levels</h1>
44
- ${content}
45
- </main>`,
46
- );
47
- }
48
-
49
- /**
50
- * Format level detail as microdata HTML
51
- * @param {Object} level - Raw level entity
52
- * @returns {string} HTML with microdata
53
- */
54
- export function levelToMicrodata(level) {
55
- const view = prepareLevelDetail(level);
56
-
57
- if (!view) return "";
58
-
59
- const sections = [];
60
-
61
- // Titles section
62
- if (view.professionalTitle || view.managementTitle) {
63
- const titlePairs = [];
64
- if (view.professionalTitle) {
65
- titlePairs.push({
66
- term: "Professional Track",
67
- definition: view.professionalTitle,
68
- itemprop: "professionalTitle",
69
- });
70
- }
71
- if (view.managementTitle) {
72
- titlePairs.push({
73
- term: "Management Track",
74
- definition: view.managementTitle,
75
- itemprop: "managementTitle",
76
- });
77
- }
78
- sections.push(section("Titles", dl(titlePairs), 2));
79
- }
80
-
81
- // Base skill proficiencies - using BaseSkillProficiencies itemtype
82
- if (
83
- view.baseSkillProficiencies &&
84
- Object.keys(view.baseSkillProficiencies).length > 0
85
- ) {
86
- const levelPairs = Object.entries(view.baseSkillProficiencies).map(
87
- ([type, level]) => ({
88
- term: formatLevelName(type),
89
- definition: formatLevelName(level),
90
- itemprop: type,
91
- }),
92
- );
93
- sections.push(
94
- section(
95
- "Base Skill Proficiencies",
96
- `${openTag("div", { itemtype: "BaseSkillProficiencies", itemprop: "baseSkillProficiencies" })}
97
- ${dl(levelPairs)}
98
- </div>`,
99
- 2,
100
- ),
101
- );
102
- }
103
-
104
- // Base behaviour maturity - link to BehaviourMaturity
105
- if (view.baseBehaviourMaturity) {
106
- const maturityContent = `${linkTag("baseBehaviourMaturity", `#${level.baseBehaviourMaturity}`)}
107
- <p>${formatLevelName(level.baseBehaviourMaturity)}</p>`;
108
- sections.push(section("Base Behaviour Maturity", maturityContent, 2));
109
- }
110
-
111
- // Expectations - using LevelExpectations itemtype
112
- if (view.expectations && Object.keys(view.expectations).length > 0) {
113
- const expectationPairs = Object.entries(view.expectations).map(
114
- ([key, value]) => ({
115
- term: formatLevelName(key),
116
- definition: value,
117
- itemprop: key,
118
- }),
119
- );
120
- sections.push(
121
- section(
122
- "Expectations",
123
- `${openTag("div", { itemtype: "LevelExpectations", itemprop: "expectations" })}
124
- ${dl(expectationPairs)}
125
- </div>`,
126
- 2,
127
- ),
128
- );
129
- }
130
-
131
- const body = `<main>
132
- ${openTag("article", { itemtype: "Level", itemid: `#${view.id}` })}
133
- <h1>${prop("span", "id", view.id)} — ${escapeHtml(view.displayName)}</h1>
134
- ${metaTag("ordinalRank", String(view.ordinalRank))}
135
- ${view.typicalExperienceRange ? prop("p", "typicalExperienceRange", `Experience: ${view.typicalExperienceRange}`) : ""}
136
- ${sections.join("\n")}
137
- </article>
138
- </main>`;
139
-
140
- return htmlDocument(`${view.id} - ${view.displayName}`, body);
141
- }
@@ -1,184 +0,0 @@
1
- /**
2
- * Shared microdata HTML utilities
3
- *
4
- * Helper functions for generating clean, class-less HTML with microdata attributes
5
- * aligned with the RDF schema at https://www.forwardimpact.team/schema/rdf/
6
- */
7
-
8
- const VOCAB_BASE = "https://www.forwardimpact.team/schema/rdf/";
9
-
10
- /**
11
- * Create an opening tag with microdata attributes
12
- * @param {string} tag - HTML tag name
13
- * @param {Object} [attrs] - Optional attributes
14
- * @param {string} [attrs.itemtype] - Microdata type (without vocab prefix)
15
- * @param {string} [attrs.itemprop] - Microdata property name
16
- * @param {string} [attrs.itemid] - Microdata item ID
17
- * @returns {string}
18
- */
19
- export function openTag(tag, attrs = {}) {
20
- const parts = [tag];
21
-
22
- if (attrs.itemtype) {
23
- parts.push(`itemscope`);
24
- parts.push(`itemtype="${VOCAB_BASE}${attrs.itemtype}"`);
25
- }
26
-
27
- if (attrs.itemprop) {
28
- parts.push(`itemprop="${attrs.itemprop}"`);
29
- }
30
-
31
- if (attrs.itemid) {
32
- parts.push(`itemid="${attrs.itemid}"`);
33
- }
34
-
35
- return `<${parts.join(" ")}>`;
36
- }
37
-
38
- /**
39
- * Create a self-closing meta element with microdata
40
- * @param {string} itemprop - Property name
41
- * @param {string} content - Content value
42
- * @returns {string}
43
- */
44
- export function metaTag(itemprop, content) {
45
- return `<meta itemprop="${itemprop}" content="${escapeAttr(content)}">`;
46
- }
47
-
48
- /**
49
- * Create a link element with microdata
50
- * @param {string} itemprop - Property name
51
- * @param {string} href - Link target
52
- * @returns {string}
53
- */
54
- export function linkTag(itemprop, href) {
55
- return `<link itemprop="${itemprop}" href="${escapeAttr(href)}">`;
56
- }
57
-
58
- /**
59
- * Wrap content in an element with itemprop
60
- * @param {string} tag - HTML tag name
61
- * @param {string} itemprop - Property name
62
- * @param {string} content - Content to wrap
63
- * @returns {string}
64
- */
65
- export function prop(tag, itemprop, content) {
66
- return `<${tag} itemprop="${itemprop}">${escapeHtml(content)}</${tag}>`;
67
- }
68
-
69
- /**
70
- * Wrap raw HTML content in an element with itemprop (no escaping)
71
- * @param {string} tag - HTML tag name
72
- * @param {string} itemprop - Property name
73
- * @param {string} html - HTML content to wrap
74
- * @returns {string}
75
- */
76
- export function propRaw(tag, itemprop, html) {
77
- return `<${tag} itemprop="${itemprop}">${html}</${tag}>`;
78
- }
79
-
80
- /**
81
- * Create a section with optional heading
82
- * @param {string} heading - Section heading text
83
- * @param {string} content - Section content
84
- * @param {number} [level=2] - Heading level (2-6)
85
- * @returns {string}
86
- */
87
- export function section(heading, content, level = 2) {
88
- const hTag = `h${Math.min(Math.max(level, 1), 6)}`;
89
- return `<section>
90
- <${hTag}>${escapeHtml(heading)}</${hTag}>
91
- ${content}
92
- </section>`;
93
- }
94
-
95
- /**
96
- * Create an unordered list
97
- * @param {string[]} items - List items (already HTML)
98
- * @param {string} [itemprop] - Optional property for list items
99
- * @returns {string}
100
- */
101
- export function ul(items, itemprop) {
102
- if (!items.length) return "";
103
- const lis = items
104
- .map((item) =>
105
- itemprop ? `<li itemprop="${itemprop}">${item}</li>` : `<li>${item}</li>`,
106
- )
107
- .join("\n");
108
- return `<ul>\n${lis}\n</ul>`;
109
- }
110
-
111
- /**
112
- * Create a definition list from key-value pairs
113
- * @param {Array<{term: string, definition: string, itemprop?: string}>} pairs
114
- * @returns {string}
115
- */
116
- export function dl(pairs) {
117
- if (!pairs.length) return "";
118
- const content = pairs
119
- .map(({ term, definition, itemprop }) => {
120
- const dd = itemprop
121
- ? `<dd itemprop="${itemprop}">${escapeHtml(definition)}</dd>`
122
- : `<dd>${escapeHtml(definition)}</dd>`;
123
- return `<dt>${escapeHtml(term)}</dt>\n${dd}`;
124
- })
125
- .join("\n");
126
- return `<dl>\n${content}\n</dl>`;
127
- }
128
-
129
- /**
130
- * Escape HTML special characters
131
- * @param {string} str
132
- * @returns {string}
133
- */
134
- export function escapeHtml(str) {
135
- if (str == null) return "";
136
- return String(str)
137
- .replace(/&/g, "&amp;")
138
- .replace(/</g, "&lt;")
139
- .replace(/>/g, "&gt;");
140
- }
141
-
142
- /**
143
- * Escape attribute value
144
- * @param {string} str
145
- * @returns {string}
146
- */
147
- export function escapeAttr(str) {
148
- if (str == null) return "";
149
- return String(str)
150
- .replace(/&/g, "&amp;")
151
- .replace(/"/g, "&quot;")
152
- .replace(/</g, "&lt;")
153
- .replace(/>/g, "&gt;");
154
- }
155
-
156
- /**
157
- * Format level name for display (capitalize, replace underscores)
158
- * @param {string} level
159
- * @returns {string}
160
- */
161
- export function formatLevelName(level) {
162
- if (!level) return "";
163
- return level.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
164
- }
165
-
166
- /**
167
- * Generate a full microdata HTML document
168
- * @param {string} title - Document title
169
- * @param {string} body - Body content
170
- * @returns {string}
171
- */
172
- export function htmlDocument(title, body) {
173
- return `<!DOCTYPE html>
174
- <html lang="en">
175
- <head>
176
- <meta charset="UTF-8">
177
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
178
- <title>${escapeHtml(title)}</title>
179
- </head>
180
- <body>
181
- ${body}
182
- </body>
183
- </html>`;
184
- }
@@ -1,151 +0,0 @@
1
- /**
2
- * Skill formatting for microdata HTML output
3
- *
4
- * Generates clean, class-less HTML with microdata aligned with capability.schema.json
5
- * RDF vocab: https://www.forwardimpact.team/schema/rdf/
6
- */
7
-
8
- import {
9
- openTag,
10
- prop,
11
- propRaw,
12
- metaTag,
13
- section,
14
- dl,
15
- ul,
16
- escapeHtml,
17
- formatLevelName,
18
- htmlDocument,
19
- } from "../microdata-shared.js";
20
- import { prepareSkillsList, prepareSkillDetail } from "./shared.js";
21
-
22
- /**
23
- * Format skill list as microdata HTML
24
- * @param {Array} skills - Raw skill entities
25
- * @param {Array} capabilities - Capability entities
26
- * @returns {string} HTML with microdata
27
- */
28
- export function skillListToMicrodata(skills, capabilities) {
29
- const { groups, groupOrder } = prepareSkillsList(skills, capabilities);
30
-
31
- const content = groupOrder
32
- .map((capability) => {
33
- const capabilitySkills = groups[capability];
34
- const skillItems = capabilitySkills
35
- .map(
36
- (
37
- skill,
38
- ) => `${openTag("article", { itemtype: "Skill", itemid: `#${skill.id}` })}
39
- ${prop("h3", "name", skill.name)}
40
- ${prop("p", "description", skill.truncatedDescription)}
41
- ${metaTag("capability", capability)}
42
- </article>`,
43
- )
44
- .join("\n");
45
-
46
- return section(formatLevelName(capability), skillItems, 2);
47
- })
48
- .join("\n");
49
-
50
- return htmlDocument(
51
- "Skills",
52
- `<main>
53
- <h1>Skills</h1>
54
- ${content}
55
- </main>`,
56
- );
57
- }
58
-
59
- /**
60
- * Format skill detail as microdata HTML
61
- * @param {Object} skill - Raw skill entity
62
- * @param {Object} context - Additional context
63
- * @param {Array} context.disciplines - All disciplines
64
- * @param {Array} context.tracks - All tracks
65
- * @param {Array} context.drivers - All drivers
66
- * @param {Array} context.capabilities - Capability entities
67
- * @returns {string} HTML with microdata
68
- */
69
- export function skillToMicrodata(
70
- skill,
71
- { disciplines, tracks, drivers, capabilities },
72
- ) {
73
- const view = prepareSkillDetail(skill, {
74
- disciplines,
75
- tracks,
76
- drivers,
77
- capabilities,
78
- });
79
-
80
- if (!view) return "";
81
-
82
- const sections = [];
83
-
84
- // Human-only badge
85
- if (view.isHumanOnly) {
86
- sections.push(`<p><strong>Human-Only</strong> — Requires interpersonal skills; excluded from agents</p>
87
- ${metaTag("isHumanOnly", "true")}`);
88
- }
89
-
90
- // Level descriptions - uses ProficiencyDescriptions itemtype
91
- const levelPairs = Object.entries(view.proficiencyDescriptions).map(
92
- ([level, desc]) => ({
93
- term: formatLevelName(level),
94
- definition: desc,
95
- itemprop: `${level}Description`,
96
- }),
97
- );
98
- sections.push(
99
- section(
100
- "Level Descriptions",
101
- `${openTag("div", { itemtype: "ProficiencyDescriptions", itemprop: "proficiencyDescriptions" })}
102
- ${dl(levelPairs)}
103
- </div>`,
104
- 2,
105
- ),
106
- );
107
-
108
- // Related disciplines
109
- if (view.relatedDisciplines.length > 0) {
110
- const disciplineItems = view.relatedDisciplines.map(
111
- (d) =>
112
- `<a href="#${escapeHtml(d.id)}">${escapeHtml(d.name)}</a> (${escapeHtml(d.skillType)})`,
113
- );
114
- sections.push(section("Used in Disciplines", ul(disciplineItems), 2));
115
- }
116
-
117
- // Related tracks with modifiers
118
- if (view.relatedTracks.length > 0) {
119
- const trackItems = view.relatedTracks.map((t) => {
120
- const modifierStr = t.modifier > 0 ? `+${t.modifier}` : `${t.modifier}`;
121
- return `<a href="#${escapeHtml(t.id)}">${escapeHtml(t.name)}</a>: ${modifierStr}`;
122
- });
123
- sections.push(section("Modified by Tracks", ul(trackItems), 2));
124
- }
125
-
126
- // Related drivers
127
- if (view.relatedDrivers.length > 0) {
128
- const driverItems = view.relatedDrivers.map(
129
- (d) => `<a href="#${escapeHtml(d.id)}">${escapeHtml(d.name)}</a>`,
130
- );
131
- sections.push(section("Linked to Drivers", ul(driverItems), 2));
132
- }
133
-
134
- const body = `<main>
135
- ${openTag("article", { itemtype: "Skill", itemid: `#${view.id}` })}
136
- ${prop("h1", "name", view.name)}
137
- ${metaTag("id", view.id)}
138
- ${metaTag("capability", view.capability)}
139
- ${propRaw(
140
- "div",
141
- "human",
142
- `${openTag("div", { itemtype: "SkillHumanSection" })}
143
- ${prop("p", "description", view.description)}
144
- ${sections.join("\n")}
145
- </div>`,
146
- )}
147
- </article>
148
- </main>`;
149
-
150
- return htmlDocument(view.name, body);
151
- }
@@ -1,116 +0,0 @@
1
- /**
2
- * Stage formatting for microdata HTML output
3
- *
4
- * Generates clean, class-less HTML with microdata aligned with stages.schema.json
5
- * RDF vocab: https://www.forwardimpact.team/schema/rdf/
6
- */
7
-
8
- import {
9
- openTag,
10
- prop,
11
- metaTag,
12
- linkTag,
13
- section,
14
- ul,
15
- escapeHtml,
16
- htmlDocument,
17
- } from "../microdata-shared.js";
18
- import { prepareStagesList, prepareStageDetail } from "./shared.js";
19
-
20
- /**
21
- * Format stage list as microdata HTML
22
- * @param {Array} stages - Raw stage entities
23
- * @returns {string} HTML with microdata
24
- */
25
- export function stageListToMicrodata(stages) {
26
- const { items } = prepareStagesList(stages);
27
-
28
- const content = items
29
- .map((stage) => {
30
- const handoffText =
31
- stage.handoffs.length > 0
32
- ? `→ ${stage.handoffs.map((h) => h.target).join(", ")}`
33
- : "";
34
- return `${openTag("article", { itemtype: "Stage", itemid: `#${stage.id}` })}
35
- ${prop("h2", "name", `${stage.emojiIcon} ${stage.name}`)}
36
- ${prop("p", "description", stage.truncatedDescription)}
37
- ${handoffText ? `<p>Handoffs: ${handoffText}</p>` : ""}
38
- </article>`;
39
- })
40
- .join("\n");
41
-
42
- return htmlDocument(
43
- "Stages",
44
- `<main>
45
- <h1>Stages</h1>
46
- ${content}
47
- </main>`,
48
- );
49
- }
50
-
51
- /**
52
- * Format stage detail as microdata HTML
53
- * @param {Object} stage - Raw stage entity
54
- * @returns {string} HTML with microdata
55
- */
56
- export function stageToMicrodata(stage) {
57
- const view = prepareStageDetail(stage);
58
-
59
- if (!view) return "";
60
-
61
- const sections = [];
62
-
63
- // Read checklist
64
- if (view.readChecklist.length > 0) {
65
- const readItems = view.readChecklist.map((c) => escapeHtml(c));
66
- sections.push(
67
- section("Read-Then-Do Checklist", ul(readItems, "readChecklist"), 2),
68
- );
69
- }
70
-
71
- // Constraints
72
- if (view.constraints.length > 0) {
73
- const constraintItems = view.constraints.map((c) => escapeHtml(c));
74
- sections.push(
75
- section("Constraints", ul(constraintItems, "constraints"), 2),
76
- );
77
- }
78
-
79
- // Confirm checklist
80
- if (view.confirmChecklist.length > 0) {
81
- const confirmItems = view.confirmChecklist.map((c) => escapeHtml(c));
82
- sections.push(
83
- section(
84
- "Do-Then-Confirm Checklist",
85
- ul(confirmItems, "confirmChecklist"),
86
- 2,
87
- ),
88
- );
89
- }
90
-
91
- // Handoffs - using Handoff itemtype
92
- if (view.handoffs.length > 0) {
93
- const handoffItems = view.handoffs.map(
94
- (
95
- h,
96
- ) => `${openTag("article", { itemtype: "Handoff", itemprop: "handoffs" })}
97
- ${linkTag("targetStage", `#${h.target}`)}
98
- <p><strong>${prop("span", "label", h.label)}</strong> → ${escapeHtml(h.target)}</p>
99
- ${prop("p", "prompt", h.prompt)}
100
- </article>`,
101
- );
102
- sections.push(section("Handoffs", handoffItems.join("\n"), 2));
103
- }
104
-
105
- const body = `<main>
106
- ${openTag("article", { itemtype: "Stage", itemid: `#${view.id}` })}
107
- ${prop("h1", "name", view.name)}
108
- ${metaTag("id", view.id)}
109
- ${stage.emojiIcon ? metaTag("emojiIcon", stage.emojiIcon) : ""}
110
- ${prop("p", "description", view.description)}
111
- ${sections.join("\n")}
112
- </article>
113
- </main>`;
114
-
115
- return htmlDocument(view.name, body);
116
- }
@@ -1,111 +0,0 @@
1
- /**
2
- * Track formatting for microdata HTML output
3
- *
4
- * Generates clean, class-less HTML with microdata aligned with track.schema.json
5
- * RDF vocab: https://www.forwardimpact.team/schema/rdf/
6
- */
7
-
8
- import {
9
- openTag,
10
- prop,
11
- metaTag,
12
- linkTag,
13
- section,
14
- ul,
15
- escapeHtml,
16
- htmlDocument,
17
- } from "../microdata-shared.js";
18
- import { prepareTracksList, prepareTrackDetail } from "./shared.js";
19
-
20
- /**
21
- * Format track list as microdata HTML
22
- * @param {Array} tracks - Raw track entities
23
- * @returns {string} HTML with microdata
24
- */
25
- export function trackListToMicrodata(tracks) {
26
- const { items } = prepareTracksList(tracks);
27
-
28
- const content = items
29
- .map((track) => {
30
- return `${openTag("article", { itemtype: "Track", itemid: `#${track.id}` })}
31
- ${prop("h2", "name", track.name)}
32
- </article>`;
33
- })
34
- .join("\n");
35
-
36
- return htmlDocument(
37
- "Tracks",
38
- `<main>
39
- <h1>Tracks</h1>
40
- ${content}
41
- </main>`,
42
- );
43
- }
44
-
45
- /**
46
- * Format track detail as microdata HTML
47
- * @param {Object} track - Raw track entity
48
- * @param {Object} context - Additional context
49
- * @param {Array} context.skills - All skills
50
- * @param {Array} context.behaviours - All behaviours
51
- * @param {Array} context.disciplines - All disciplines
52
- * @returns {string} HTML with microdata
53
- */
54
- export function trackToMicrodata(track, { skills, behaviours, disciplines }) {
55
- const view = prepareTrackDetail(track, { skills, behaviours, disciplines });
56
-
57
- if (!view) return "";
58
-
59
- const sections = [];
60
-
61
- // Skill modifiers - using SkillModifier itemtype with targetCapability
62
- if (view.skillModifiers.length > 0) {
63
- const modifierItems = view.skillModifiers.map((m) => {
64
- const modifierStr = m.modifier > 0 ? `+${m.modifier}` : `${m.modifier}`;
65
-
66
- if (m.isCapability && m.skills && m.skills.length > 0) {
67
- // Capability with expanded skills
68
- const skillLinks = m.skills
69
- .map(
70
- (s) => `<a href="#${escapeHtml(s.id)}">${escapeHtml(s.name)}</a>`,
71
- )
72
- .join(", ");
73
- return `${openTag("div", { itemtype: "SkillModifier", itemprop: "skillModifiers" })}
74
- ${linkTag("targetCapability", `#${m.id}`)}
75
- <strong>${escapeHtml(m.name)} Capability</strong> (${openTag("span", { itemprop: "modifierValue" })}${modifierStr}</span>)
76
- <p>${skillLinks}</p>
77
- </div>`;
78
- } else {
79
- // Individual skill or capability without skills
80
- return `${openTag("span", { itemtype: "SkillModifier", itemprop: "skillModifiers" })}
81
- ${linkTag("targetCapability", `#${m.id}`)}
82
- <strong>${escapeHtml(m.name)}</strong>: ${openTag("span", { itemprop: "modifierValue" })}${modifierStr}</span>
83
- </span>`;
84
- }
85
- });
86
- sections.push(section("Skill Modifiers", modifierItems.join("\n"), 2));
87
- }
88
-
89
- // Behaviour modifiers - using BehaviourModifier itemtype
90
- if (view.behaviourModifiers.length > 0) {
91
- const modifierItems = view.behaviourModifiers.map((b) => {
92
- const modifierStr = b.modifier > 0 ? `+${b.modifier}` : `${b.modifier}`;
93
- return `${openTag("span", { itemtype: "BehaviourModifier", itemprop: "behaviourModifiers" })}
94
- ${linkTag("targetBehaviour", `#${b.id}`)}
95
- <a href="#${escapeHtml(b.id)}">${escapeHtml(b.name)}</a>: ${openTag("span", { itemprop: "modifierValue" })}${modifierStr}</span>
96
- </span>`;
97
- });
98
- sections.push(section("Behaviour Modifiers", ul(modifierItems), 2));
99
- }
100
-
101
- const body = `<main>
102
- ${openTag("article", { itemtype: "Track", itemid: `#${view.id}` })}
103
- ${prop("h1", "name", view.name)}
104
- ${metaTag("id", view.id)}
105
- ${prop("p", "description", view.description)}
106
- ${sections.join("\n")}
107
- </article>
108
- </main>`;
109
-
110
- return htmlDocument(view.name, body);
111
- }