@heroiclands/package-build 22.10.0 → 22.11.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 +6 -0
- package/CONTENT.md +21 -0
- package/docs/api.md +1 -0
- package/docs/commands.md +1 -1
- package/docs/content-format.md +28 -2
- package/engine/address-fields.mjs +31 -9
- package/engine/base-compiler.mjs +2 -0
- package/engine/field-spec.mjs +3 -2
- package/engine/frontmatter-lint.mjs +6 -4
- package/engine/note-addresses.mjs +17 -2
- package/engine/note-vocabulary.mjs +0 -7
- package/package.json +1 -1
- package/sohl/infobox.mjs +31 -0
- package/sohl/item-fields.mjs +17 -0
- package/types/engine/address-fields.d.mts +5 -0
- package/types/engine/field-spec.d.mts +6 -4
- package/types/sohl/infobox.d.mts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 22.11.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- **Affiliations:** SoHL common skills link to their exact native Items; authors declare them in the SoHL block, and shared declarations receive an error.
|
|
8
|
+
|
|
3
9
|
## 22.10.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
package/CONTENT.md
CHANGED
|
@@ -1444,6 +1444,27 @@ segment, and returns a complete tuple or a reason. This mode leaves package
|
|
|
1444
1444
|
membership to resolution. The lint receives the builder's package separately
|
|
1445
1445
|
from target lookup, including when reference resolution is disabled.
|
|
1446
1446
|
|
|
1447
|
+
An affiliation authors native SoHL skill references at
|
|
1448
|
+
`sohl.system.commonSkills`:
|
|
1449
|
+
|
|
1450
|
+
```yaml
|
|
1451
|
+
sohl:
|
|
1452
|
+
system:
|
|
1453
|
+
commonSkills:
|
|
1454
|
+
- herb
|
|
1455
|
+
- sohl-sohl-skill-law
|
|
1456
|
+
```
|
|
1457
|
+
|
|
1458
|
+
The first Address names this content package's SoHL skill; the second names a
|
|
1459
|
+
skill in the declared SoHL dependency. The content index retains their complete
|
|
1460
|
+
Addresses. Foundry compilation resolves each exact native skill Item in the
|
|
1461
|
+
local or declared dependency index and writes its published Item UUID to the
|
|
1462
|
+
affiliation's `system.commonSkills` array. The producer's module ID and pack
|
|
1463
|
+
come from that index. An unresolved target or a target without a native Item
|
|
1464
|
+
UUID is a located error. The shared `data:` container has no `commonSkills`
|
|
1465
|
+
property. Infoboxes use a skill's published documentation journal when it has
|
|
1466
|
+
one and show its known name as plain text otherwise.
|
|
1467
|
+
|
|
1447
1468
|
**There is deliberately no third rule** requiring every note to repeat its own
|
|
1448
1469
|
`type-shortcode` address in `aliases:`. It would serve one reader — Obsidian, so
|
|
1449
1470
|
`[[type-shortcode]]` resolves in the editor — and no build reads it: both
|
package/docs/api.md
CHANGED
|
@@ -1369,6 +1369,7 @@ Which of SoHL's facts a note's summary panel carries, and how they group. Read o
|
|
|
1369
1369
|
| `SOHL_FIELD_PRESENTATION` | `const SOHL_FIELD_PRESENTATION` | — | looking up what one of SoHL's fields is called, or why it carries no row |
|
|
1370
1370
|
| `PROTECTION_FIELDS` | `const PROTECTION_FIELDS` | — | reading the declarations of the aspects armour is rated against, in the order shown |
|
|
1371
1371
|
| `UNSTATED` | `const UNSTATED` | — | naming what a strike mode shows where a value was not stated |
|
|
1372
|
+
| `affiliationSections` | `affiliationSections(fm, ctx)` | `object[]` | building an affiliation's box with skill documentation links |
|
|
1372
1373
|
| `armorSections` | `armorSections(fm, ctx)` | `object[]` | building armour's box, protection included |
|
|
1373
1374
|
| `beingSections` | `beingSections(fm, ctx)` | `object[]` | building a being's attributes, skills, mystical abilities and equipment |
|
|
1374
1375
|
| `decodeItem` | `decodeItem(entry)` | `object \| undefined` | reading what one `sohl.items` entry names, whichever form it was written in |
|
package/docs/commands.md
CHANGED
|
@@ -1445,7 +1445,7 @@ declaration that compiles it. Otherwise 0.
|
|
|
1445
1445
|
$ content-build content-format fields --fields sohl --coverage
|
|
1446
1446
|
[…] 13 type(s) compared against sohl's declarations (56 field pair(s)).
|
|
1447
1447
|
[…] 12 type(s) the format declares are out of reach — no `itemBuilders` entry covers them: being, homepage, vehicle, armorlocation, lore, map, place, scenario, doc, macro, bundle, folder.
|
|
1448
|
-
[…] affiliation: format only [
|
|
1448
|
+
[…] affiliation: format only [demonym, economy, epithet, governance, lore, population, symbol, templatePriority], declaration only [commonSkills, level, office, society, subType, title]
|
|
1449
1449
|
```
|
|
1450
1450
|
|
|
1451
1451
|
**SEE ALSO**
|
package/docs/content-format.md
CHANGED
|
@@ -495,6 +495,12 @@ reduction happens at build time, and two things are errors naming the note: an
|
|
|
495
495
|
Address whose type the field does not accept, and two Addresses that reduce to one
|
|
496
496
|
shortcode, which the field cannot hold both of.
|
|
497
497
|
|
|
498
|
+
**A SoHL skill Address becomes a native Item UUID.** An affiliation's
|
|
499
|
+
`sohl.system.commonSkills` names skills in its own package or a declared
|
|
500
|
+
dependency. Its `system.commonSkills` contains the exact published Item UUIDs,
|
|
501
|
+
including each foreign module's own pack name. Native skill lookup uses those
|
|
502
|
+
UUIDs; the journal, website and book use documentation links for presentation.
|
|
503
|
+
|
|
498
504
|
**A `Code` suffix marks a different thing.** `system.assocSkillCode`,
|
|
499
505
|
`system.assocAffiliationCode` and `system.parentSkillCode` hold a `Shortcode` at
|
|
500
506
|
both ends — each is authored as one, under `<system>.system`, and emitted verbatim
|
|
@@ -2844,7 +2850,6 @@ rank names the standing, and the standing says.
|
|
|
2844
2850
|
| `governance.summary` | `string` | summary of the governance situation |
|
|
2845
2851
|
| `governance.ranks` | `Rank[]` | The ranks available to members of the affiliation |
|
|
2846
2852
|
| `governance.offices` | `Map<name, description>` | Official offices in the affiliation |
|
|
2847
|
-
| `commonSkills` | `Address[]` | Common skills among members (languages, etc.) |
|
|
2848
2853
|
| `seat` | `Address` | Where the affiliation's authority sits |
|
|
2849
2854
|
| `domains` | `Address[]` | Places over which this affiliation holds sway |
|
|
2850
2855
|
| `population` | `number` | Number of people in the affiliation (precision 2 significant digits). |
|
|
@@ -2926,6 +2931,27 @@ If `sohl` is present, this becomes an `affiliation` item.
|
|
|
2926
2931
|
| `data.parents` | `system.parents` | NA |
|
|
2927
2932
|
| `data.relations` | `system.relations` | NA |
|
|
2928
2933
|
|
|
2934
|
+
An affiliation writes SoHL common skills under `sohl.system.commonSkills` as a
|
|
2935
|
+
list of skill Addresses. An abbreviated value names a skill in the authoring
|
|
2936
|
+
package; a skill in the SoHL system package uses its complete Address:
|
|
2937
|
+
|
|
2938
|
+
```yaml
|
|
2939
|
+
sohl:
|
|
2940
|
+
system:
|
|
2941
|
+
commonSkills:
|
|
2942
|
+
- herb
|
|
2943
|
+
- sohl-sohl-skill-law
|
|
2944
|
+
```
|
|
2945
|
+
|
|
2946
|
+
The compiled SoHL Item stores the exact published Item UUIDs in
|
|
2947
|
+
`system.commonSkills`. An empty or absent list emits `[]`. A foreign target must
|
|
2948
|
+
come from a declared `relationships.systems` or `relationships.requires` content
|
|
2949
|
+
index. Each UUID uses the module and pack named by that target's published index;
|
|
2950
|
+
a missing skill Item or Item UUID is an error at the authored Address. The
|
|
2951
|
+
content index retains complete Addresses. Journal, web and book infoboxes link
|
|
2952
|
+
to a skill's published documentation journal when one exists and otherwise show
|
|
2953
|
+
its name as plain text.
|
|
2954
|
+
|
|
2929
2955
|
`governance` reaches no system field. Ranks and offices are the note's and the
|
|
2930
2956
|
web page's — SoHL's affiliation item has nowhere to put them, and inventing a
|
|
2931
2957
|
mapping for a field no schema declares is the drift these tables exist to catch.
|
|
@@ -3903,7 +3929,7 @@ because they were true of something else, and each removal has a home to go to.
|
|
|
3903
3929
|
|
|
3904
3930
|
**`languages` is a fact about a polity.** A place's languages change when its
|
|
3905
3931
|
ruler changes, which is what makes them the ruler's property — and
|
|
3906
|
-
`
|
|
3932
|
+
`sohl.system.commonSkills` holds them. The authored corpus agrees: of 206
|
|
3907
3933
|
places carrying `languages`, 190 were settlements and 16 were regions, and not one
|
|
3908
3934
|
was a site, a structure or a feature. A ruin has no language.
|
|
3909
3935
|
|
|
@@ -12,16 +12,18 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* **An authored Address, an emitted
|
|
15
|
+
* **An authored Address, an emitted native reference.**
|
|
16
16
|
*
|
|
17
17
|
* A declared field may hold an Address where the document it compiles into
|
|
18
|
-
* holds a **Shortcode
|
|
18
|
+
* holds a **Shortcode** or a published Item UUID. An affiliation's `seat` is a Shortcode case: the note
|
|
19
19
|
* names a place at whatever length says what it means — `tashal`,
|
|
20
20
|
* `place-tashal`, `none-place-tashal`, `kethira-none-place-tashal` — and the
|
|
21
21
|
* compiled Item carries `tashal`, which is what the runtime resolves among the
|
|
22
22
|
* items one actor holds, where packages do not exist.
|
|
23
23
|
*
|
|
24
|
-
*
|
|
24
|
+
* The `emit: "uuid"` declaration resolves an exact native target from the
|
|
25
|
+
* local or declared dependency index. The target supplies its published UUID.
|
|
26
|
+
* This module performs both conversions at the Foundry output boundary.
|
|
25
27
|
* Storing the written form instead would store a value plus a context that does
|
|
26
28
|
* not travel with it: a four-segment string reaches a runtime that looks up one
|
|
27
29
|
* segment and returns nothing, silently.
|
|
@@ -70,10 +72,8 @@
|
|
|
70
72
|
*
|
|
71
73
|
* ## What this module does not do
|
|
72
74
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* and nothing else. Whether the target exists is a separate question, asked
|
|
76
|
-
* where the index is.
|
|
75
|
+
* Shortcode output needs only the Address grammar. UUID output requires the
|
|
76
|
+
* exact target in the published index and accepts only Item UUIDs.
|
|
77
77
|
*
|
|
78
78
|
* A failure is a **finding**, never a diagnostic: the caller owns the file and
|
|
79
79
|
* the severity, and locates the finding at the value inside the note.
|
|
@@ -107,6 +107,7 @@ export const ADDRESS_FIELD_SHAPES = Object.freeze(["value", "items", "keys"]);
|
|
|
107
107
|
* @property {readonly string[]} accepts - Every type the position accepts.
|
|
108
108
|
* @property {"value"|"items"|"keys"} [holds] - Where the Addresses are.
|
|
109
109
|
* Defaults to `value`.
|
|
110
|
+
* @property {"uuid"} [emit] - Resolve each exact target to its published Item UUID.
|
|
110
111
|
*/
|
|
111
112
|
|
|
112
113
|
/**
|
|
@@ -171,7 +172,7 @@ function reduceOne(value, field, vocabulary, findings) {
|
|
|
171
172
|
// Two keys, or two list entries, reducing to one emitted shortcode: the
|
|
172
173
|
// Address each of them named, by the shortcode all of them land on.
|
|
173
174
|
const seen = new Map();
|
|
174
|
-
const read = (written) => readOne(written, field, defaults, seen, findings, shape);
|
|
175
|
+
const read = (written) => readOne(written, field, defaults, seen, findings, shape, vocabulary);
|
|
175
176
|
|
|
176
177
|
if (shape === "keys") {
|
|
177
178
|
if (!isMapping(value)) return undefined;
|
|
@@ -212,8 +213,15 @@ function reduceOne(value, field, vocabulary, findings) {
|
|
|
212
213
|
* @returns {string|undefined} The shortcode, or `undefined` when nothing is
|
|
213
214
|
* emitted for this value.
|
|
214
215
|
*/
|
|
215
|
-
function readOne(written, field, defaults, seen, findings, shape) {
|
|
216
|
+
function readOne(written, field, defaults, seen, findings, shape, vocabulary) {
|
|
216
217
|
const key = findingKey(field);
|
|
218
|
+
// An embedded Item may inherit an already compiled reference from its model.
|
|
219
|
+
if (
|
|
220
|
+
field.address.emit === "uuid" &&
|
|
221
|
+
typeof written === "string" &&
|
|
222
|
+
/^Compendium\.[^.]+\.[^.]+\.Item\.[^.]+$/.test(written)
|
|
223
|
+
)
|
|
224
|
+
return written;
|
|
217
225
|
const value =
|
|
218
226
|
isAddressTuple(written) ? renderAddress(written)
|
|
219
227
|
: typeof written === "string" ? written
|
|
@@ -231,6 +239,20 @@ function readOne(written, field, defaults, seen, findings, shape) {
|
|
|
231
239
|
}
|
|
232
240
|
|
|
233
241
|
const address = renderAddress(tuple);
|
|
242
|
+
if (field.address.emit === "uuid") {
|
|
243
|
+
const target =
|
|
244
|
+
tuple.package === vocabulary.package ?
|
|
245
|
+
vocabulary.referenceTargets?.get(address)
|
|
246
|
+
: vocabulary.foreignReferences?.get(address);
|
|
247
|
+
const uuid = target?.uuid;
|
|
248
|
+
if (!uuid || !/^Compendium\.[^.]+\.[^.]+\.Item\.[^.]+$/.test(uuid)) {
|
|
249
|
+
report(
|
|
250
|
+
`\`${key}\` names \`${address}\`, but its exact native Item has no published Item UUID`,
|
|
251
|
+
);
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
return uuid;
|
|
255
|
+
}
|
|
234
256
|
const code = tuple.shortcode;
|
|
235
257
|
const first = seen.get(code);
|
|
236
258
|
if (first && first.address !== address) {
|
package/engine/base-compiler.mjs
CHANGED
|
@@ -717,6 +717,8 @@ export class BasePackCompiler {
|
|
|
717
717
|
types: this.linkIndex?.types,
|
|
718
718
|
packages: this.linkIndex?.packages,
|
|
719
719
|
noIndexPackages: this.linkIndex?.noIndexPackages,
|
|
720
|
+
referenceTargets: this.linkIndex?.referenceTargets,
|
|
721
|
+
foreignReferences: this.linkIndex?.foreignReferences,
|
|
720
722
|
});
|
|
721
723
|
for (const finding of findings) {
|
|
722
724
|
this.errorCount++;
|
package/engine/field-spec.mjs
CHANGED
|
@@ -148,8 +148,9 @@ export { legacyKeyOf, retiredTopLevelKey, setPath };
|
|
|
148
148
|
* being's own body structure, not a note, so it declares none. Distinct
|
|
149
149
|
* from a `DataFieldSpec`'s `ref` ({@link module:engine/note-vocabulary}),
|
|
150
150
|
* which names an **Address** instead.
|
|
151
|
-
* @property {import("./address-fields.mjs").AddressPosition} [address] -
|
|
152
|
-
* authored value is an Address
|
|
151
|
+
* @property {import("./address-fields.mjs").AddressPosition} [address] - The
|
|
152
|
+
* authored value is an Address. The emitted value is its Shortcode, or the
|
|
153
|
+
* exact published Item UUID when `emit: "uuid"` is declared. The
|
|
153
154
|
* statement carries the default type an omitted `<type>` segment takes, the
|
|
154
155
|
* set of types the position accepts — two separate things, which coincide at
|
|
155
156
|
* most positions — and where the Addresses sit: the value itself, every
|
|
@@ -476,10 +476,12 @@ function checkDataContainer(note, { type, fields, packs, addressContext }) {
|
|
|
476
476
|
...positionOfFrontmatterPath(raw, ["data", key], { key: true }),
|
|
477
477
|
severity: "error",
|
|
478
478
|
message:
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
`not
|
|
482
|
-
|
|
479
|
+
type === "affiliation" && key === "commonSkills" ?
|
|
480
|
+
"`data.commonSkills` is not an affiliation field; write skill Addresses at `sohl.system.commonSkills`"
|
|
481
|
+
: `"${key}" is not a \`data:\` property declared by ${type}; ` +
|
|
482
|
+
`the container is closed, so unlike a top-level key it is ` +
|
|
483
|
+
`not passed through to the page` +
|
|
484
|
+
(guess ? `. Did you mean "${guess}"?` : ""),
|
|
483
485
|
});
|
|
484
486
|
}
|
|
485
487
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { systemBlocksFor } from "./system-vocabulary.mjs";
|
|
8
|
+
import { declaredSystems, systemBlocksFor } from "./system-vocabulary.mjs";
|
|
9
9
|
import { RETIRED_FIELD_ALIASES } from "./retired-fields.mjs";
|
|
10
10
|
import {
|
|
11
11
|
isAddressTuple,
|
|
@@ -260,10 +260,25 @@ export function decodeNoteAddresses(fm, context) {
|
|
|
260
260
|
* @returns {object} Builder defaults and system declarations.
|
|
261
261
|
*/
|
|
262
262
|
export function noteAddressContext(config) {
|
|
263
|
+
const systemBlocks = { ...systemBlocksFor(config) };
|
|
264
|
+
// A module with one declared system uses its flat Item registry for that
|
|
265
|
+
// system's Address positions, including fields stored in its content index.
|
|
266
|
+
const systems = new Set([
|
|
267
|
+
...declaredSystems(config),
|
|
268
|
+
...(config.relationships?.systems ?? []).map((relationship) => relationship.id),
|
|
269
|
+
]);
|
|
270
|
+
if (
|
|
271
|
+
systems.size === 1 &&
|
|
272
|
+
config.itemFields &&
|
|
273
|
+
Object.keys(config.itemFieldsBySystem ?? {}).length === 0
|
|
274
|
+
) {
|
|
275
|
+
const [system] = systems;
|
|
276
|
+
systemBlocks[system] = { ...systemBlocks[system], fields: config.itemFields };
|
|
277
|
+
}
|
|
263
278
|
return {
|
|
264
279
|
package: config.contentPackage,
|
|
265
280
|
system: "none",
|
|
266
|
-
systemBlocks
|
|
281
|
+
systemBlocks,
|
|
267
282
|
};
|
|
268
283
|
}
|
|
269
284
|
|
|
@@ -761,13 +761,6 @@ export const NOTE_VOCABULARY = Object.freeze({
|
|
|
761
761
|
...ANY,
|
|
762
762
|
describe: "The named offices it appoints, each with what the office does.",
|
|
763
763
|
},
|
|
764
|
-
{
|
|
765
|
-
name: "commonSkills",
|
|
766
|
-
...LINKS,
|
|
767
|
-
ref: "skill",
|
|
768
|
-
accepts: ["skill"],
|
|
769
|
-
describe: "Skills common among members — languages first among them.",
|
|
770
|
-
},
|
|
771
764
|
{
|
|
772
765
|
name: "seat",
|
|
773
766
|
...LINK,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "22.
|
|
3
|
+
"version": "22.11.0",
|
|
4
4
|
"description": "Shared toolchain for building and shipping a HeroicLands Foundry VTT package — content compilation, manifest, localization, staging, bundle, release and deployment.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
package/sohl/infobox.mjs
CHANGED
|
@@ -543,6 +543,36 @@ function genericSections(fm, type, ctx) {
|
|
|
543
543
|
return systemRowsSection(fm, NOTE_SCHEMAS[type], ctx);
|
|
544
544
|
}
|
|
545
545
|
|
|
546
|
+
/** An affiliation presents skill documentation while retaining native Item identity. */
|
|
547
|
+
export function affiliationSections(fm, ctx) {
|
|
548
|
+
const field = NOTE_SCHEMAS.affiliation.find((entry) => entry.to === "commonSkills");
|
|
549
|
+
const sections = systemRowsSection(
|
|
550
|
+
fm,
|
|
551
|
+
NOTE_SCHEMAS.affiliation.filter((entry) => entry !== field),
|
|
552
|
+
ctx,
|
|
553
|
+
);
|
|
554
|
+
const { value } = ctx.resolveField(field, fm, { block: ctx.block });
|
|
555
|
+
if (!Array.isArray(value) || value.length === 0) return sections;
|
|
556
|
+
const entries = value.map((skill) => {
|
|
557
|
+
const native = ctx.resolve?.(skill, { type: "skill", system: "sohl" });
|
|
558
|
+
const docAddress =
|
|
559
|
+
isAddressTuple(skill) ? completeAddress({ ...skill, system: "none" }) : undefined;
|
|
560
|
+
const doc = docAddress ? ctx.resolve?.(docAddress, { type: docAddress.type }) : undefined;
|
|
561
|
+
return {
|
|
562
|
+
text: doc?.name || native?.name || humanizeValue(skill),
|
|
563
|
+
...(doc?.url ? { url: doc.url } : {}),
|
|
564
|
+
...(doc?.uuid ? { uuid: doc.uuid } : {}),
|
|
565
|
+
...(docAddress && (doc?.url || doc?.uuid) ? { address: docAddress } : {}),
|
|
566
|
+
};
|
|
567
|
+
});
|
|
568
|
+
sections.push({
|
|
569
|
+
id: "commonskills",
|
|
570
|
+
layout: "rows",
|
|
571
|
+
rows: [{ label: "Common skills", kind: "links", value: entries }],
|
|
572
|
+
});
|
|
573
|
+
return sections;
|
|
574
|
+
}
|
|
575
|
+
|
|
546
576
|
/**
|
|
547
577
|
* SoHL's infobox declaration.
|
|
548
578
|
*
|
|
@@ -555,6 +585,7 @@ export const SOHL_INFOBOX = defineInfobox({
|
|
|
555
585
|
presentation: SOHL_FIELD_PRESENTATION,
|
|
556
586
|
sections: {
|
|
557
587
|
being: beingSections,
|
|
588
|
+
affiliation: affiliationSections,
|
|
558
589
|
armorgear: armorSections,
|
|
559
590
|
weapongear: weaponSections,
|
|
560
591
|
projectilegear: projectileSections,
|
package/sohl/item-fields.mjs
CHANGED
|
@@ -178,6 +178,14 @@ const AFFILIATION_ITEMS = Object.freeze({
|
|
|
178
178
|
/** A map keyed by affiliation: this one's standing toward each. */
|
|
179
179
|
const AFFILIATION_KEYS = Object.freeze({ ...AFFILIATION_ITEMS, holds: "keys" });
|
|
180
180
|
|
|
181
|
+
/** Native skill references retain the published Item UUID. */
|
|
182
|
+
const SKILL_UUID_ITEMS = Object.freeze({
|
|
183
|
+
type: "skill",
|
|
184
|
+
accepts: Object.freeze(["skill"]),
|
|
185
|
+
holds: "items",
|
|
186
|
+
emit: "uuid",
|
|
187
|
+
});
|
|
188
|
+
|
|
181
189
|
/** Standings toward other affiliations, validated against the closed list. */
|
|
182
190
|
const RELATION = Object.freeze({
|
|
183
191
|
shape: "map of affiliation address → standing",
|
|
@@ -385,6 +393,15 @@ export const ITEM_FIELDS = Object.freeze({
|
|
|
385
393
|
default: 0,
|
|
386
394
|
describe: "Standing within the society.",
|
|
387
395
|
},
|
|
396
|
+
{
|
|
397
|
+
name: "commonSkills",
|
|
398
|
+
to: "commonSkills",
|
|
399
|
+
...ADDRESS_LIST,
|
|
400
|
+
address: SKILL_UUID_ITEMS,
|
|
401
|
+
default: [],
|
|
402
|
+
topLevelMeans: "Common skills belong to the SoHL affiliation item.",
|
|
403
|
+
describe: "Native SoHL skill Items common among members, by address.",
|
|
404
|
+
},
|
|
388
405
|
{
|
|
389
406
|
// Plural because the field holds a map of many standings, one per
|
|
390
407
|
// affiliation — the singular reads as a misnomer.
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* @property {readonly string[]} accepts - Every type the position accepts.
|
|
7
7
|
* @property {"value"|"items"|"keys"} [holds] - Where the Addresses are.
|
|
8
8
|
* Defaults to `value`.
|
|
9
|
+
* @property {"uuid"} [emit] - Resolve each exact target to its published Item UUID.
|
|
9
10
|
*/
|
|
10
11
|
/**
|
|
11
12
|
* What a reduction could not do, for a caller to report.
|
|
@@ -69,6 +70,10 @@ export type AddressPosition = {
|
|
|
69
70
|
* Defaults to `value`.
|
|
70
71
|
*/
|
|
71
72
|
holds?: "keys" | "value" | "items" | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* - Resolve each exact target to its published Item UUID.
|
|
75
|
+
*/
|
|
76
|
+
emit?: "uuid" | undefined;
|
|
72
77
|
};
|
|
73
78
|
/**
|
|
74
79
|
* What a reduction could not do, for a caller to report.
|
|
@@ -277,8 +277,9 @@ export function runtimeOnlyFields(fields: readonly FieldSpec[]): FieldSpec[];
|
|
|
277
277
|
* being's own body structure, not a note, so it declares none. Distinct
|
|
278
278
|
* from a `DataFieldSpec`'s `ref` ({@link module:engine/note-vocabulary}),
|
|
279
279
|
* which names an **Address** instead.
|
|
280
|
-
* @property {import("./address-fields.mjs").AddressPosition} [address] -
|
|
281
|
-
* authored value is an Address
|
|
280
|
+
* @property {import("./address-fields.mjs").AddressPosition} [address] - The
|
|
281
|
+
* authored value is an Address. The emitted value is its Shortcode, or the
|
|
282
|
+
* exact published Item UUID when `emit: "uuid"` is declared. The
|
|
282
283
|
* statement carries the default type an omitted `<type>` segment takes, the
|
|
283
284
|
* set of types the position accepts — two separate things, which coincide at
|
|
284
285
|
* most positions — and where the Addresses sit: the value itself, every
|
|
@@ -553,8 +554,9 @@ export type FieldSpec = {
|
|
|
553
554
|
*/
|
|
554
555
|
code?: string | undefined;
|
|
555
556
|
/**
|
|
556
|
-
* -
|
|
557
|
-
* authored value is an Address
|
|
557
|
+
* - The
|
|
558
|
+
* authored value is an Address. The emitted value is its Shortcode, or the
|
|
559
|
+
* exact published Item UUID when `emit: "uuid"` is declared. The
|
|
558
560
|
* statement carries the default type an omitted `<type>` segment takes, the
|
|
559
561
|
* set of types the position accepts — two separate things, which coincide at
|
|
560
562
|
* most positions — and where the Addresses sit: the value itself, every
|
package/types/sohl/infobox.d.mts
CHANGED
|
@@ -82,6 +82,8 @@ export function projectileSections(fm: object, ctx: object): object[];
|
|
|
82
82
|
* @returns {[string, object][]} Mode name → the mode.
|
|
83
83
|
*/
|
|
84
84
|
export function strikeModes(declared: unknown): [string, object][];
|
|
85
|
+
/** An affiliation presents skill documentation while retaining native Item identity. */
|
|
86
|
+
export function affiliationSections(fm: any, ctx: any): object[];
|
|
85
87
|
/** What this system's box is called. @type {string} */
|
|
86
88
|
export const SOHL_INFOBOX_TITLE: string;
|
|
87
89
|
/**
|