@nstarkman/data-resolver 20261023.0.0 → 20261028.0.0
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/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/src/collect.js +2 -1
- package/src/inline.js +56 -5
- package/src/money.js +16 -0
- package/src/store.js +6 -9
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
Versions are dates, `YYYYMMDD.0.N`; the four research-kit packages are released together at one
|
|
4
4
|
version. A breaking change says how to migrate.
|
|
5
5
|
|
|
6
|
+
## 20261028.0.0
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- Released with `@nstarkman/collaborator-map` 20261028.0.0. `collect()` is unchanged: a paper's
|
|
11
|
+
snapshot keeps the people among its authors, which now includes the site's author.
|
|
12
|
+
|
|
13
|
+
## 20261023.0.1
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- `collect()` put the co-authors of every item it reached on the paper's collaborator map — its
|
|
18
|
+
packages' papers' too; it now puts only the paper's own co-authors.
|
|
19
|
+
|
|
6
20
|
## 20261023.0.0
|
|
7
21
|
|
|
8
22
|
First release.
|
package/package.json
CHANGED
package/src/collect.js
CHANGED
|
@@ -53,7 +53,8 @@ export async function collect(id, reader, { card } = {}) {
|
|
|
53
53
|
|
|
54
54
|
const config = Object.fromEntries(await Promise.all(CONFIG.map(async (k) => [k, JSON.parse(await reader.read(`config/${k}.json`))])));
|
|
55
55
|
const all = [...items.values()];
|
|
56
|
-
|
|
56
|
+
// The map is the paper's own co-authors: not those of the papers it reaches (its packages').
|
|
57
|
+
const orcids = new Set((root.authors ?? []).map((a) => a.orcid).filter(Boolean));
|
|
57
58
|
const people = config.collaborators.people.filter((p) => orcids.has(p.orcid));
|
|
58
59
|
const orgs = new Set([
|
|
59
60
|
...people.flatMap((p) => p.affiliations.map((a) => a.organization)),
|
package/src/inline.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { dateLabel } from '@nstarkman/research-store';
|
|
12
12
|
import { sortAchievements } from './mentees.js';
|
|
13
|
+
import { money } from './money.js';
|
|
13
14
|
|
|
14
15
|
// `[text](https://…)` for the web, and `[text](item:some-id)` to point at
|
|
15
16
|
// another entry on the same CV — the CV cross-references itself (a position
|
|
@@ -75,16 +76,66 @@ export function lines(long, base = '') {
|
|
|
75
76
|
* The extra lines go last, after the thesis and supervisors, so a line index
|
|
76
77
|
* means the same thing whether or not they are included — the builder ticks
|
|
77
78
|
* lines by index, and inserting in the middle would silently move its ticks.
|
|
79
|
+
* A talk's title (`talk`) is the exception that proves it: it comes first, in
|
|
80
|
+
* quotation marks, because it is what a presentation's first line always was.
|
|
78
81
|
*/
|
|
79
|
-
export function detailLines(item, { complete = false } = {}) {
|
|
82
|
+
export function detailLines(item, { complete = false, currency, forms = false } = {}) {
|
|
83
|
+
const prose = (long) => amountLines(long, item, { currency, forms }).map(noFinalPeriod);
|
|
80
84
|
return [
|
|
81
|
-
...
|
|
82
|
-
...(item.
|
|
83
|
-
...(item.
|
|
84
|
-
...(
|
|
85
|
+
...(item.talk ? [quoted(item.talk)] : []),
|
|
86
|
+
...prose(item.details),
|
|
87
|
+
...(item.thesis ? [noFinalPeriod(spans(`Thesis: ${item.thesis}`))] : []),
|
|
88
|
+
...(item.supervisors?.length ? [noFinalPeriod(spans(`Supervisors: ${item.supervisors.join(', ')}`))] : []),
|
|
89
|
+
...(complete ? prose(item.detailsComplete) : []),
|
|
85
90
|
];
|
|
86
91
|
}
|
|
87
92
|
|
|
93
|
+
/**
|
|
94
|
+
* A title as a line in quotation marks: “…”. The marks belong to the rendering, not the record,
|
|
95
|
+
* and a double quote inside the title becomes a single one. The closing mark follows the title
|
|
96
|
+
* without its final period, as any line under a heading has none.
|
|
97
|
+
*/
|
|
98
|
+
export function quoted(title) {
|
|
99
|
+
const inner = title.replace(/["“]([^"”]*)["”]/g, '‘$1’');
|
|
100
|
+
return [{ t: '“' }, ...noFinalPeriod(spans(inner)), { t: '”' }];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* A CV line is a label or a phrase under a heading, so it does not end in a period: the
|
|
105
|
+
* last span loses one. A line ending in an abbreviation ("et al.", "etc.") or an ellipsis
|
|
106
|
+
* keeps it. The record is not changed, only what is shown.
|
|
107
|
+
*/
|
|
108
|
+
export const KEEPS_PERIOD = /(\.\.|\b(?:al|etc|Jr|Sr|Inc|vs|St|Co|Ltd)\.)$/;
|
|
109
|
+
export function noFinalPeriod(line) {
|
|
110
|
+
const last = line[line.length - 1];
|
|
111
|
+
if (!last || !last.t) return line;
|
|
112
|
+
const t = last.t.trimEnd();
|
|
113
|
+
if (!t.endsWith('.') || KEEPS_PERIOD.test(t)) return line;
|
|
114
|
+
const rest = t.slice(0, -1);
|
|
115
|
+
return rest ? [...line.slice(0, -1), { ...last, t: rest }] : line.slice(0, -1);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* `lines`, with `{amount}` standing for the item's `amount` written as `money` writes
|
|
120
|
+
* it in `currency`: so a figure in prose is stored once, in the data, and follows
|
|
121
|
+
* the CV's currency style. With `forms` the span carries all three forms, for the
|
|
122
|
+
* builder's page to swap; a token on an item with no `amount` is left as it is,
|
|
123
|
+
* which the data tests refuse.
|
|
124
|
+
*/
|
|
125
|
+
function amountLines(long, item, { currency, forms }) {
|
|
126
|
+
const token = '{amount}';
|
|
127
|
+
return (Array.isArray(long) ? long : [long]).filter(Boolean).map((line) => {
|
|
128
|
+
if (!item.amount || !line.includes(token)) return spans(line);
|
|
129
|
+
return line.split(token).flatMap((piece, k) => [
|
|
130
|
+
...(k ? [{
|
|
131
|
+
t: money(item.amount, currency),
|
|
132
|
+
...(forms && { forms: { code: money(item.amount), us: money(item.amount, 'us'), dollar: money(item.amount, 'dollar') } }),
|
|
133
|
+
}] : []),
|
|
134
|
+
...spans(piece),
|
|
135
|
+
]);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
88
139
|
/**
|
|
89
140
|
* A mentee's marked achievements, one span array per line, oldest first so the
|
|
90
141
|
* entry reads as a progression. They follow everything `detailLines` returns,
|
package/src/money.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// How an `amount` is written. Pure, and its own module so the store and the prose
|
|
2
|
+
// expansion in inline.js can both use it without importing each other.
|
|
3
|
+
|
|
4
|
+
/** By style: `us` replaces the D with a sign, `dollar` gives US dollars the bare
|
|
5
|
+
* sign. A currency with no sign keeps its code. */
|
|
6
|
+
const SIGNS = { us: { USD: 'US$', CAD: 'CA$' }, dollar: { USD: '$', CAD: 'CA$' } };
|
|
7
|
+
|
|
8
|
+
/** "USD 10,000", "CAD 20,000–30,000 p.a.", or null; in a `style`, "US$10,000",
|
|
9
|
+
* "$10,000", "CA$20,000–30,000 p.a.". The code is the default. */
|
|
10
|
+
export function money(a, style) {
|
|
11
|
+
if (!a) return null;
|
|
12
|
+
const n = (v) => v.toLocaleString('en-US');
|
|
13
|
+
const span = a.valueMax ? `${n(a.value)}–${n(a.valueMax)}` : n(a.value);
|
|
14
|
+
const sign = SIGNS[style]?.[a.currency];
|
|
15
|
+
return `${sign ?? `${a.currency} `}${span}${a.perAnnum ? ' p.a.' : ''}`;
|
|
16
|
+
}
|
package/src/store.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// given — the website's whole data/ (its src/lib/data.js), or the slice of it a paper
|
|
4
4
|
// site keeps (collect.js). Moved here from src/lib/data.js unchanged but for that.
|
|
5
5
|
|
|
6
|
+
import { money } from './money.js';
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* Partial dates (YYYY, YYYY-MM, YYYY-MM-DD) compare correctly as strings, so a
|
|
8
10
|
* month orders items inside a year even though only the year is ever shown.
|
|
@@ -291,14 +293,6 @@ export function createStore({ items: given, lists = new Map(), config }) {
|
|
|
291
293
|
return stops;
|
|
292
294
|
}
|
|
293
295
|
|
|
294
|
-
/** "USD 10,000", "CAD 20,000\u201330,000 p.a.", or null. */
|
|
295
|
-
function money(a) {
|
|
296
|
-
if (!a) return null;
|
|
297
|
-
const n = (v) => v.toLocaleString('en-US');
|
|
298
|
-
const span = a.valueMax ? `${n(a.value)}\u2013${n(a.valueMax)}` : n(a.value);
|
|
299
|
-
return `${a.currency} ${span}${a.perAnnum ? ' p.a.' : ''}`;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
296
|
/** "The Astrophysical Journal 979, 155"; short, "ApJ 979, 155", where
|
|
303
297
|
* config/journals.json has the journal's short name. */
|
|
304
298
|
function venueLine(item, { short = false } = {}) {
|
|
@@ -355,6 +349,9 @@ export function createStore({ items: given, lists = new Map(), config }) {
|
|
|
355
349
|
event: 'link', homepage: 'link',
|
|
356
350
|
};
|
|
357
351
|
|
|
352
|
+
/** A link's mark: its rel's, but a school's homepage is a graduation cap. */
|
|
353
|
+
const linkIcon = (item, l) => (item.type === 'education' && l.rel === 'homepage' ? 'cap' : REL_ICON[relKey(l)] ?? 'link');
|
|
354
|
+
|
|
358
355
|
/**
|
|
359
356
|
* The one link a title points at: the article, else the preprint, else its
|
|
360
357
|
* ADS record, else null. The ADS link is synthesised as a `paper` rel, so it
|
|
@@ -465,5 +462,5 @@ export function createStore({ items: given, lists = new Map(), config }) {
|
|
|
465
462
|
return { start, end };
|
|
466
463
|
}
|
|
467
464
|
|
|
468
|
-
return { items, lists, config, byType, featured, resolve, titleOf, splitTitle, HIGHLIGHT_TOPICS, SOFTWARE_GROUPS, displayName, authorPosition, venueUrl, softwarePapers, softwareUsed, affiliationAt, authors, positionStops, money, venueLine, year, dateLabel, relKey, REL_ICON, primaryLink, links, assists, assistItem, groupedByDefault, institutionGroups, groupTitle, groupDate };
|
|
465
|
+
return { items, lists, config, byType, featured, resolve, titleOf, splitTitle, HIGHLIGHT_TOPICS, SOFTWARE_GROUPS, displayName, authorPosition, venueUrl, softwarePapers, softwareUsed, affiliationAt, authors, positionStops, money, venueLine, year, dateLabel, relKey, REL_ICON, linkIcon, primaryLink, links, assists, assistItem, groupedByDefault, institutionGroups, groupTitle, groupDate };
|
|
469
466
|
}
|