@heroiclands/package-build 19.0.0 → 20.2.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 +1100 -0
- package/CONTENT.md +264 -33
- package/README.md +43 -4
- package/bin/content-build.mjs +94 -3
- package/config.mjs +9 -1
- package/content-config.mjs +99 -19
- package/docs/content-format.md +394 -72
- package/e2e.mjs +297 -3
- package/engine/actor-compiler.mjs +197 -7
- package/engine/address-charset.mjs +23 -5
- package/engine/base-compiler.mjs +63 -2
- package/engine/bundles.mjs +9 -0
- package/engine/content-address.mjs +92 -1
- package/engine/content-charset.mjs +434 -0
- package/engine/content-format.mjs +102 -0
- package/engine/content-icons.mjs +388 -0
- package/engine/content-index.mjs +11 -8
- package/engine/content-links.mjs +37 -21
- package/engine/field-reference.mjs +57 -5
- package/engine/field-spec.mjs +214 -7
- package/engine/folder-notes.mjs +24 -1
- package/engine/foreign-catalog.mjs +112 -7
- package/engine/foundry-entries.mjs +14 -0
- package/engine/frontmatter-lint.mjs +377 -56
- package/engine/frontmatter.mjs +11 -11
- package/engine/generate.mjs +72 -12
- package/engine/helpers.mjs +96 -10
- package/engine/index.mjs +9 -0
- package/engine/item-compiler.mjs +37 -0
- package/engine/journals.mjs +21 -4
- package/engine/macros.mjs +8 -0
- package/engine/map-notes.mjs +7 -7
- package/engine/note-claims.mjs +208 -5
- package/engine/note-ids.mjs +25 -1
- package/engine/note-vocabulary.mjs +76 -9
- package/engine/pack-config.mjs +102 -12
- package/engine/pack-router.mjs +0 -0
- package/engine/prose-config.mjs +42 -0
- package/engine/prose-lint.mjs +126 -0
- package/engine/retired-fields.mjs +57 -16
- package/engine/runtime-only-fields.mjs +204 -0
- package/engine/scenes.mjs +12 -19
- package/engine/schema-check.mjs +23 -1
- package/engine/schema-extract.mjs +13 -0
- package/engine/site-index.mjs +17 -0
- package/engine/subtype-registry.mjs +30 -0
- package/engine/system-block.mjs +81 -3
- package/engine/web-wikilinks.mjs +33 -27
- package/engine/wikilink-syntax.mjs +7 -0
- package/engine/wikilinks.mjs +74 -16
- package/hm3/actors.mjs +63 -13
- package/package.json +2 -2
- package/sohl/actors.mjs +106 -7
- package/sohl/item-fields.mjs +203 -0
- package/sohl/note-schemas.mjs +6 -3
- package/types/config.d.mts +7 -0
- package/types/e2e.d.mts +130 -3
- package/types/engine/actor-compiler.d.mts +83 -3
- package/types/engine/address-charset.d.mts +22 -4
- package/types/engine/base-compiler.d.mts +54 -3
- package/types/engine/content-address.d.mts +64 -0
- package/types/engine/content-charset.d.mts +127 -0
- package/types/engine/content-format.d.mts +9 -0
- package/types/engine/content-icons.d.mts +151 -0
- package/types/engine/field-spec.d.mts +271 -3
- package/types/engine/folder-notes.d.mts +20 -0
- package/types/engine/foreign-catalog.d.mts +38 -2
- package/types/engine/foundry-entries.d.mts +6 -0
- package/types/engine/frontmatter-lint.d.mts +164 -30
- package/types/engine/frontmatter.d.mts +11 -11
- package/types/engine/generate.d.mts +27 -0
- package/types/engine/helpers.d.mts +45 -9
- package/types/engine/index.d.mts +3 -0
- package/types/engine/map-notes.d.mts +2 -2
- package/types/engine/note-claims.d.mts +67 -0
- package/types/engine/note-ids.d.mts +14 -0
- package/types/engine/pack-config.d.mts +35 -0
- package/types/engine/prose-config.d.mts +41 -0
- package/types/engine/prose-lint.d.mts +36 -0
- package/types/engine/retired-fields.d.mts +29 -13
- package/types/engine/runtime-only-fields.d.mts +102 -0
- package/types/engine/schema-check.d.mts +10 -1
- package/types/engine/subtype-registry.d.mts +21 -0
- package/types/engine/system-block.d.mts +28 -2
- package/types/sohl/actors.d.mts +3 -3
|
@@ -17,6 +17,26 @@
|
|
|
17
17
|
* @returns {boolean} True when the value came from the retiring position.
|
|
18
18
|
*/
|
|
19
19
|
export function readsLegacyKey(field: FieldSpec, from: import("./system-block.mjs").FieldSource): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Whether a resolution read a field from the top-level key `data:` gathered it
|
|
22
|
+
* off — the shared level's retiring position (#332).
|
|
23
|
+
*
|
|
24
|
+
* {@link readsLegacyKey}'s sibling, and the same signal: a finding here counts
|
|
25
|
+
* one note still on the pre-`data:` spelling, so the sweep has something to
|
|
26
|
+
* count down instead of a corpus nobody has surveyed.
|
|
27
|
+
*
|
|
28
|
+
* The `from` tag already carries the whole answer — step 3b is the only thing
|
|
29
|
+
* that produces it, and it produces it only for a `data.` source — so this is a
|
|
30
|
+
* name for the question rather than a second test of it. Named all the same,
|
|
31
|
+
* because a compile-time report and the frontmatter lint both ask it and must
|
|
32
|
+
* not drift apart about what counts.
|
|
33
|
+
*
|
|
34
|
+
* @param {FieldSpec} field - The declaration.
|
|
35
|
+
* @param {import("./system-block.mjs").FieldSource} from - Where
|
|
36
|
+
* {@link resolveFieldValue} said the value came from.
|
|
37
|
+
* @returns {boolean} True when the value came from the retiring top-level key.
|
|
38
|
+
*/
|
|
39
|
+
export function readsRetiredTopLevel(field: FieldSpec, from: import("./system-block.mjs").FieldSource): boolean;
|
|
20
40
|
/**
|
|
21
41
|
* Read one declared field out of a note's frontmatter.
|
|
22
42
|
*
|
|
@@ -38,12 +58,77 @@ export function readsLegacyKey(field: FieldSpec, from: import("./system-block.mj
|
|
|
38
58
|
* rather than a returned list because the caller is a compiler, which already
|
|
39
59
|
* knows the note and how to locate a key in it; this module knows neither and
|
|
40
60
|
* would have to invent a finding shape to say so.
|
|
61
|
+
* @param {(field: FieldSpec) => void} [options.onRetiredTopLevel] - Called with
|
|
62
|
+
* each field read from the top-level key `data:` gathered it off (#332). The
|
|
63
|
+
* shared level's counterpart to `onLegacyKey`, and a separate callback
|
|
64
|
+
* because it is a separate position: a note may have moved one of the two and
|
|
65
|
+
* not the other, and a caller that conflated them would tell its author to
|
|
66
|
+
* fix the wrong line.
|
|
41
67
|
* @returns {any} The value to emit.
|
|
42
68
|
*/
|
|
43
|
-
export function readField(field: FieldSpec, fm: object,
|
|
69
|
+
export function readField(field: FieldSpec, fm: object, options?: {
|
|
44
70
|
block?: string | undefined;
|
|
45
71
|
onLegacyKey?: ((field: FieldSpec) => void) | undefined;
|
|
72
|
+
onRetiredTopLevel?: ((field: FieldSpec) => void) | undefined;
|
|
46
73
|
}): any;
|
|
74
|
+
/**
|
|
75
|
+
* The same read, reporting **where the value came from** as well.
|
|
76
|
+
*
|
|
77
|
+
* {@link readField} answers "what does this field hold", which is what almost
|
|
78
|
+
* every caller wants. A builder has one further question — *should the key be
|
|
79
|
+
* written at all* — and it cannot be answered from the value: `null` from a
|
|
80
|
+
* note and `null` from a declared default are the same value and opposite
|
|
81
|
+
* facts (#329).
|
|
82
|
+
*
|
|
83
|
+
* So the position rides back beside the value, resolved **once**. The
|
|
84
|
+
* alternative is a builder that calls {@link resolveFieldValue} for the source
|
|
85
|
+
* and {@link readField} for the value, which resolves the position twice and
|
|
86
|
+
* states in two places the rule that a field is authored in exactly one.
|
|
87
|
+
*
|
|
88
|
+
* @param {FieldSpec} field - The declaration.
|
|
89
|
+
* @param {object} fm - The note's frontmatter.
|
|
90
|
+
* @param {object} [options] - Options, as {@link readField} takes them.
|
|
91
|
+
* @param {string} [options.block="sohl"] - Which system's block to resolve
|
|
92
|
+
* against.
|
|
93
|
+
* @param {(field: FieldSpec) => void} [options.onLegacyKey] - See
|
|
94
|
+
* {@link readField}.
|
|
95
|
+
* @param {(field: FieldSpec) => void} [options.onRetiredTopLevel] - See
|
|
96
|
+
* {@link readField}.
|
|
97
|
+
* @returns {{value: any, from: import("./system-block.mjs").FieldSource}} The
|
|
98
|
+
* value to emit, and the position it was read from.
|
|
99
|
+
*/
|
|
100
|
+
export function readFieldEntry(field: FieldSpec, fm: object, { block, onLegacyKey, onRetiredTopLevel }?: {
|
|
101
|
+
block?: string | undefined;
|
|
102
|
+
onLegacyKey?: ((field: FieldSpec) => void) | undefined;
|
|
103
|
+
onRetiredTopLevel?: ((field: FieldSpec) => void) | undefined;
|
|
104
|
+
}): {
|
|
105
|
+
value: any;
|
|
106
|
+
from: import("./system-block.mjs").FieldSource;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Whether a note supplied a value for a field, as opposed to a default doing it.
|
|
110
|
+
*
|
|
111
|
+
* The question {@link FieldSpec.omitWhenAbsent} turns on, asked of the
|
|
112
|
+
* *position* rather than of the value — which cannot answer it, since a
|
|
113
|
+
* declared `default: null` and an authored `null` are indistinguishable once
|
|
114
|
+
* the value is in hand (#329).
|
|
115
|
+
*
|
|
116
|
+
* `undefined` counts as absent whatever position reported it, because writing
|
|
117
|
+
* the key then emits a value `JSON.stringify` drops — the key present in the
|
|
118
|
+
* object and absent from the pack, which is the sort of disagreement this
|
|
119
|
+
* package exists to remove. It arrives from one place: the in-block step
|
|
120
|
+
* answers `value ?? field.default` for a key authored as `null`, so a field
|
|
121
|
+
* declaring no default resolves through "authored" to nothing at all. A field
|
|
122
|
+
* that also declares a `read` never reaches this, since its coercion has by
|
|
123
|
+
* then turned the `undefined` into whatever it makes of an absent value —
|
|
124
|
+
* ordinarily `null`, which is a value the note asked for and is emitted.
|
|
125
|
+
*
|
|
126
|
+
* @param {import("./system-block.mjs").FieldSource} from - Where
|
|
127
|
+
* {@link resolveFieldValue} said the value came from.
|
|
128
|
+
* @param {any} value - The value it gave back.
|
|
129
|
+
* @returns {boolean} True when the note wrote one.
|
|
130
|
+
*/
|
|
131
|
+
export function isAuthored(from: import("./system-block.mjs").FieldSource, value: any): boolean;
|
|
47
132
|
/**
|
|
48
133
|
* Turn a field declaration into the builder it declares.
|
|
49
134
|
*
|
|
@@ -55,11 +140,15 @@ export function readField(field: FieldSpec, fm: object, { block, onLegacyKey }?:
|
|
|
55
140
|
* @param {(field: FieldSpec) => void} [options.onLegacyKey] - Passed through to
|
|
56
141
|
* {@link readField}: called with each field the note authored at the position
|
|
57
142
|
* it is being swept off (#305).
|
|
143
|
+
* @param {(field: FieldSpec) => void} [options.onRetiredTopLevel] - Passed
|
|
144
|
+
* through to {@link readField}: called with each field the note authored at
|
|
145
|
+
* the top-level key `data:` gathered it off (#332).
|
|
58
146
|
* @returns {(fm: object) => object} A `system`-block builder.
|
|
59
147
|
*/
|
|
60
|
-
export function buildFromFields(fields: readonly FieldSpec[], { block, onLegacyKey }?: {
|
|
148
|
+
export function buildFromFields(fields: readonly FieldSpec[], { block, onLegacyKey, onRetiredTopLevel }?: {
|
|
61
149
|
block?: string | undefined;
|
|
62
150
|
onLegacyKey?: ((field: FieldSpec) => void) | undefined;
|
|
151
|
+
onRetiredTopLevel?: ((field: FieldSpec) => void) | undefined;
|
|
63
152
|
}): (fm: object) => object;
|
|
64
153
|
/**
|
|
65
154
|
* The fields of a declaration an author actually writes.
|
|
@@ -73,6 +162,20 @@ export function buildFromFields(fields: readonly FieldSpec[], { block, onLegacyK
|
|
|
73
162
|
* @returns {FieldSpec[]} Only the fields with a frontmatter `name`.
|
|
74
163
|
*/
|
|
75
164
|
export function authoredFields(fields: readonly FieldSpec[]): FieldSpec[];
|
|
165
|
+
/**
|
|
166
|
+
* The fields of a declaration a note may **never** write.
|
|
167
|
+
*
|
|
168
|
+
* The complement of {@link authoredFields} in the direction that matters: those
|
|
169
|
+
* are the fields an author may write, these are the ones authoring is an error
|
|
170
|
+
* (#330). Everything else in a declaration — a constant, a derived value — is
|
|
171
|
+
* simply not authored, which is a statement about the *builder* rather than
|
|
172
|
+
* about the author, and says nothing about what happens if a note writes the
|
|
173
|
+
* path anyway.
|
|
174
|
+
*
|
|
175
|
+
* @param {readonly FieldSpec[]} fields - The declaration.
|
|
176
|
+
* @returns {FieldSpec[]} Only the fields declaring `runtimeOnly`.
|
|
177
|
+
*/
|
|
178
|
+
export function runtimeOnlyFields(fields: readonly FieldSpec[]): FieldSpec[];
|
|
76
179
|
/**
|
|
77
180
|
* @typedef {object} FieldSpec
|
|
78
181
|
* @property {string} to - Dotted path in the emitted `system` block — and,
|
|
@@ -135,6 +238,13 @@ export function authoredFields(fields: readonly FieldSpec[]): FieldSpec[];
|
|
|
135
238
|
* describe the *document* rather than the note: `<system>.system.<to>` and
|
|
136
239
|
* the legacy in-block `<system>.<name>`. Absent means the ordinary case —
|
|
137
240
|
* the top level is read, as the third step.
|
|
241
|
+
*
|
|
242
|
+
* **It is read from the other side too**, because the statement is symmetric:
|
|
243
|
+
* if the two positions hold unrelated quantities then the *in-block* position
|
|
244
|
+
* is not the note-level field either, so a check about the note-level field
|
|
245
|
+
* reads past it. `engine/frontmatter-lint.mjs` resolves that through
|
|
246
|
+
* `collidingBlockKeys`. Read for the emitted field alone until #312, which is
|
|
247
|
+
* how an affiliation's office style came to answer for its page heading.
|
|
138
248
|
* @property {string} [shape] - Human-readable shape, for documentation. Comes
|
|
139
249
|
* paired with `read` from one of the coercion constants below.
|
|
140
250
|
* @property {(raw: any, ctx: {fm: object, field: FieldSpec}) => any} [read] -
|
|
@@ -164,6 +274,78 @@ export function authoredFields(fields: readonly FieldSpec[]): FieldSpec[];
|
|
|
164
274
|
* note, so it declares none.
|
|
165
275
|
* @property {any|((fm: object) => any)} [value] - For a field with no `name`:
|
|
166
276
|
* the constant, or a function deriving it from the frontmatter.
|
|
277
|
+
* @property {boolean} [omitWhenAbsent] - **The key is left out entirely when
|
|
278
|
+
* the note does not carry the field** (#329), rather than written from a
|
|
279
|
+
* declared default.
|
|
280
|
+
*
|
|
281
|
+
* Every other field answers absence with a value: an unauthored `weight` is
|
|
282
|
+
* `0`, an unauthored `seat` is `null`. That is right wherever the type has an
|
|
283
|
+
* opinion about the empty case. It is wrong wherever the **DataModel** is the
|
|
284
|
+
* one holding the answer — an affliction's `onsetDurationFormula` has no
|
|
285
|
+
* compile-time value, and writing `null` over it does not merely fail to
|
|
286
|
+
* help: it makes "the author said none" and "the author said nothing"
|
|
287
|
+
* indistinguishable to every reader downstream, and it overwrites an
|
|
288
|
+
* `initial` the system chose on purpose.
|
|
289
|
+
*
|
|
290
|
+
* It is the other conditional row of the same table
|
|
291
|
+
* {@link FieldSpec.runtimeOnly} completes, and the two differ only in what
|
|
292
|
+
* they do about an *authored* value:
|
|
293
|
+
*
|
|
294
|
+
* | declaration | authored | absent |
|
|
295
|
+
* | --- | --- | --- |
|
|
296
|
+
* | ordinary | emitted | default written |
|
|
297
|
+
* | `omitWhenAbsent` | emitted | key omitted |
|
|
298
|
+
* | `runtimeOnly` | refused | key omitted |
|
|
299
|
+
*
|
|
300
|
+
* **A field declaring it must declare no `default`**, and the two are
|
|
301
|
+
* contradictory rather than merely redundant — a default is a value for the
|
|
302
|
+
* absent case, which is the case this says has none. Nor may it be combined
|
|
303
|
+
* with `required` (which fails the build on absence, so nothing is ever
|
|
304
|
+
* omitted) or with `runtimeOnly` (which is never emitted at all). The shipped
|
|
305
|
+
* declarations are checked for all three in `tests/item-fields.test.ts`.
|
|
306
|
+
*
|
|
307
|
+
* Unlike `runtimeOnly` this is a flag rather than a reason, because there is
|
|
308
|
+
* only ever one reason and no message prints it: the DataModel's `initial`
|
|
309
|
+
* stands. What an author needs to know is *that* the field has no default,
|
|
310
|
+
* which the generated reference states in the field's own row.
|
|
311
|
+
* @property {string} [runtimeOnly] - **What the field holds once play has
|
|
312
|
+
* started** — declared on a field the *document* writes for itself, which no
|
|
313
|
+
* note may author (#330).
|
|
314
|
+
*
|
|
315
|
+
* A schema declares plenty of fields a compiled document has no business
|
|
316
|
+
* carrying: an affliction's `onsetDate` is the world time its onset fired
|
|
317
|
+
* at, crystallized when the phase runs. World time does not exist while
|
|
318
|
+
* content is compiled, so there is no authoring-time value — and `0` is
|
|
319
|
+
* itself a valid world time, which is why such a field is nullable rather
|
|
320
|
+
* than sentinelled and why a default cannot stand in for one.
|
|
321
|
+
*
|
|
322
|
+
* Nothing used to stop a note writing one. The three checks that might have
|
|
323
|
+
* each declined for its own correct reason —
|
|
324
|
+
* {@link module:engine/system-block.unknownBlockKeys} reads the block's top
|
|
325
|
+
* level and never descends into `system:`;
|
|
326
|
+
* {@link module:engine/system-block.mergeSystemData} passes through every
|
|
327
|
+
* authored path no declared field claims; and the schema check's fatal
|
|
328
|
+
* direction is *undeclared*, which a field the schema really does declare
|
|
329
|
+
* satisfies. What was missing was a rule saying "declared by the system,
|
|
330
|
+
* but never authorable", and this is it.
|
|
331
|
+
*
|
|
332
|
+
* Declaring it does two things, which are the two directions of one fact:
|
|
333
|
+
*
|
|
334
|
+
* | declaration | authored | absent |
|
|
335
|
+
* | --- | --- | --- |
|
|
336
|
+
* | ordinary | emitted | default written |
|
|
337
|
+
* | runtime-only | **refused** | key omitted |
|
|
338
|
+
*
|
|
339
|
+
* The refusal is {@link module:engine/runtime-only-fields.assertNoRuntimeOnlyFields}'s;
|
|
340
|
+
* the omission is {@link buildFromFields}'s. A runtime-only entry declares a
|
|
341
|
+
* `to` and **no `name`**, so it stays out of {@link authoredFields} and every
|
|
342
|
+
* author-facing surface built on it, while still giving `mergeSystemData` a
|
|
343
|
+
* claimed path and the refusal something to name.
|
|
344
|
+
*
|
|
345
|
+
* **The value is the reason**, as {@link FieldSpec.topLevelMeans}'s is: a
|
|
346
|
+
* boolean would record the decision and lose the case for it, and the reason
|
|
347
|
+
* is what the refusal's message and the generated reference both print. It
|
|
348
|
+
* completes the sentence "it holds …".
|
|
167
349
|
* @property {string} describe - One line, for the author-facing reference.
|
|
168
350
|
*/
|
|
169
351
|
/** Whatever the author wrote, unconverted. */
|
|
@@ -290,6 +472,13 @@ export type FieldSpec = {
|
|
|
290
472
|
* describe the *document* rather than the note: `<system>.system.<to>` and
|
|
291
473
|
* the legacy in-block `<system>.<name>`. Absent means the ordinary case —
|
|
292
474
|
* the top level is read, as the third step.
|
|
475
|
+
*
|
|
476
|
+
* **It is read from the other side too**, because the statement is symmetric:
|
|
477
|
+
* if the two positions hold unrelated quantities then the *in-block* position
|
|
478
|
+
* is not the note-level field either, so a check about the note-level field
|
|
479
|
+
* reads past it. `engine/frontmatter-lint.mjs` resolves that through
|
|
480
|
+
* `collidingBlockKeys`. Read for the emitted field alone until #312, which is
|
|
481
|
+
* how an affiliation's office style came to answer for its page heading.
|
|
293
482
|
*/
|
|
294
483
|
topLevelMeans?: string | undefined;
|
|
295
484
|
/**
|
|
@@ -348,11 +537,90 @@ export type FieldSpec = {
|
|
|
348
537
|
* the constant, or a function deriving it from the frontmatter.
|
|
349
538
|
*/
|
|
350
539
|
value?: any | ((fm: object) => any);
|
|
540
|
+
/**
|
|
541
|
+
* - **The key is left out entirely when
|
|
542
|
+
* the note does not carry the field** (#329), rather than written from a
|
|
543
|
+
* declared default.
|
|
544
|
+
*
|
|
545
|
+
* Every other field answers absence with a value: an unauthored `weight` is
|
|
546
|
+
* `0`, an unauthored `seat` is `null`. That is right wherever the type has an
|
|
547
|
+
* opinion about the empty case. It is wrong wherever the **DataModel** is the
|
|
548
|
+
* one holding the answer — an affliction's `onsetDurationFormula` has no
|
|
549
|
+
* compile-time value, and writing `null` over it does not merely fail to
|
|
550
|
+
* help: it makes "the author said none" and "the author said nothing"
|
|
551
|
+
* indistinguishable to every reader downstream, and it overwrites an
|
|
552
|
+
* `initial` the system chose on purpose.
|
|
553
|
+
*
|
|
554
|
+
* It is the other conditional row of the same table
|
|
555
|
+
* {@link FieldSpec.runtimeOnly} completes, and the two differ only in what
|
|
556
|
+
* they do about an *authored* value:
|
|
557
|
+
*
|
|
558
|
+
* | declaration | authored | absent |
|
|
559
|
+
* | --- | --- | --- |
|
|
560
|
+
* | ordinary | emitted | default written |
|
|
561
|
+
* | `omitWhenAbsent` | emitted | key omitted |
|
|
562
|
+
* | `runtimeOnly` | refused | key omitted |
|
|
563
|
+
*
|
|
564
|
+
* **A field declaring it must declare no `default`**, and the two are
|
|
565
|
+
* contradictory rather than merely redundant — a default is a value for the
|
|
566
|
+
* absent case, which is the case this says has none. Nor may it be combined
|
|
567
|
+
* with `required` (which fails the build on absence, so nothing is ever
|
|
568
|
+
* omitted) or with `runtimeOnly` (which is never emitted at all). The shipped
|
|
569
|
+
* declarations are checked for all three in `tests/item-fields.test.ts`.
|
|
570
|
+
*
|
|
571
|
+
* Unlike `runtimeOnly` this is a flag rather than a reason, because there is
|
|
572
|
+
* only ever one reason and no message prints it: the DataModel's `initial`
|
|
573
|
+
* stands. What an author needs to know is *that* the field has no default,
|
|
574
|
+
* which the generated reference states in the field's own row.
|
|
575
|
+
*/
|
|
576
|
+
omitWhenAbsent?: boolean | undefined;
|
|
577
|
+
/**
|
|
578
|
+
* - **What the field holds once play has
|
|
579
|
+
* started** — declared on a field the *document* writes for itself, which no
|
|
580
|
+
* note may author (#330).
|
|
581
|
+
*
|
|
582
|
+
* A schema declares plenty of fields a compiled document has no business
|
|
583
|
+
* carrying: an affliction's `onsetDate` is the world time its onset fired
|
|
584
|
+
* at, crystallized when the phase runs. World time does not exist while
|
|
585
|
+
* content is compiled, so there is no authoring-time value — and `0` is
|
|
586
|
+
* itself a valid world time, which is why such a field is nullable rather
|
|
587
|
+
* than sentinelled and why a default cannot stand in for one.
|
|
588
|
+
*
|
|
589
|
+
* Nothing used to stop a note writing one. The three checks that might have
|
|
590
|
+
* each declined for its own correct reason —
|
|
591
|
+
* {@link module :engine/system-block.unknownBlockKeys} reads the block's top
|
|
592
|
+
* level and never descends into `system:`;
|
|
593
|
+
* {@link module :engine/system-block.mergeSystemData} passes through every
|
|
594
|
+
* authored path no declared field claims; and the schema check's fatal
|
|
595
|
+
* direction is *undeclared*, which a field the schema really does declare
|
|
596
|
+
* satisfies. What was missing was a rule saying "declared by the system,
|
|
597
|
+
* but never authorable", and this is it.
|
|
598
|
+
*
|
|
599
|
+
* Declaring it does two things, which are the two directions of one fact:
|
|
600
|
+
*
|
|
601
|
+
* | declaration | authored | absent |
|
|
602
|
+
* | --- | --- | --- |
|
|
603
|
+
* | ordinary | emitted | default written |
|
|
604
|
+
* | runtime-only | **refused** | key omitted |
|
|
605
|
+
*
|
|
606
|
+
* The refusal is {@link module :engine/runtime-only-fields.assertNoRuntimeOnlyFields}'s;
|
|
607
|
+
* the omission is {@link buildFromFields}'s. A runtime-only entry declares a
|
|
608
|
+
* `to` and **no `name`**, so it stays out of {@link authoredFields} and every
|
|
609
|
+
* author-facing surface built on it, while still giving `mergeSystemData` a
|
|
610
|
+
* claimed path and the refusal something to name.
|
|
611
|
+
*
|
|
612
|
+
* **The value is the reason**, as {@link FieldSpec.topLevelMeans}'s is: a
|
|
613
|
+
* boolean would record the decision and lose the case for it, and the reason
|
|
614
|
+
* is what the refusal's message and the generated reference both print. It
|
|
615
|
+
* completes the sentence "it holds …".
|
|
616
|
+
*/
|
|
617
|
+
runtimeOnly?: string | undefined;
|
|
351
618
|
/**
|
|
352
619
|
* - One line, for the author-facing reference.
|
|
353
620
|
*/
|
|
354
621
|
describe: string;
|
|
355
622
|
};
|
|
356
623
|
import { legacyKeyOf } from "./system-block.mjs";
|
|
624
|
+
import { retiredTopLevelKey } from "./system-block.mjs";
|
|
357
625
|
import { setPath } from "./system-block.mjs";
|
|
358
|
-
export { legacyKeyOf, setPath };
|
|
626
|
+
export { legacyKeyOf, retiredTopLevelKey, setPath };
|
|
@@ -18,6 +18,26 @@ export function bareAddress(value: string | null): string | null;
|
|
|
18
18
|
* @returns {string} `<pkg>-none-folder-<shortcode>`.
|
|
19
19
|
*/
|
|
20
20
|
export function folderAddress(pkg: string, shortcode: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* The Foundry `_id` a folder note's documents are filed under.
|
|
23
|
+
*
|
|
24
|
+
* **The one derivation, because two passes need the same answer.** The folder
|
|
25
|
+
* pass hashes it here on its way to emitting the `Folder` documents; the
|
|
26
|
+
* content index publishes it, through `noteDocId`, for a consumer who will
|
|
27
|
+
* never run this build. Deriving it twice is how the index came to publish a
|
|
28
|
+
* plausible-looking id that resolved to nothing — the general id rule hashes a
|
|
29
|
+
* note's address under the `document` namespace, and a folder's is hashed under
|
|
30
|
+
* {@link FOLDER_ID_NAMESPACE} (#310).
|
|
31
|
+
*
|
|
32
|
+
* An **authored `id` still wins**, and is applied by the caller: this is the
|
|
33
|
+
* derivation, not the resolution, so the pin rule stays stated once, where
|
|
34
|
+
* every other note type states it ({@link module:engine/note-ids.noteDocId}).
|
|
35
|
+
*
|
|
36
|
+
* @param {string} pkg - The content package.
|
|
37
|
+
* @param {string} shortcode - The folder's shortcode.
|
|
38
|
+
* @returns {string} The folder's 16-character Foundry id.
|
|
39
|
+
*/
|
|
40
|
+
export function folderDocId(pkg: string, shortcode: string): string;
|
|
21
41
|
/**
|
|
22
42
|
* Collect every folder note in a content tree.
|
|
23
43
|
*
|
|
@@ -32,6 +32,25 @@ export function catalogDir(config: object, id: string, version: string): string;
|
|
|
32
32
|
* @returns {string} The path, whether or not it exists.
|
|
33
33
|
*/
|
|
34
34
|
export function cachedSchemaPath(config: object, id: string, version: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* What to record about the packs being extracted.
|
|
37
|
+
*
|
|
38
|
+
* Exported so the pair is one fact: {@link foreignItemCatalogDirs} reads what
|
|
39
|
+
* this writes, and a test that hand-wrote the file would prove the reader
|
|
40
|
+
* against a transcription of the format rather than against the format.
|
|
41
|
+
*
|
|
42
|
+
* A pack declaring no `system` records `null` — Foundry requires the field on
|
|
43
|
+
* an Item pack, so this is the shape of a manifest that is wrong rather than a
|
|
44
|
+
* case with a meaning, and `null` reads as "neutral", which is the safe way to
|
|
45
|
+
* be wrong: a neutral pack is read by every system rather than by none.
|
|
46
|
+
*
|
|
47
|
+
* @param {readonly object[]} itemPacks - The manifest's Item pack entries.
|
|
48
|
+
* @returns {Array<{name: string, system: string|null}>} What each one is.
|
|
49
|
+
*/
|
|
50
|
+
export function itemPackManifest(itemPacks: readonly object[]): Array<{
|
|
51
|
+
name: string;
|
|
52
|
+
system: string | null;
|
|
53
|
+
}>;
|
|
35
54
|
/**
|
|
36
55
|
* Write an unzipped archive's entries under `dest`.
|
|
37
56
|
*
|
|
@@ -159,10 +178,27 @@ export function fetchAllCatalogs(config: object): Promise<number>;
|
|
|
159
178
|
* Reads the cache only. A cold cache is an error naming the command that fills
|
|
160
179
|
* it, rather than a download nobody asked for.
|
|
161
180
|
*
|
|
181
|
+
* **Scoped to one system when the caller compiles for one (#58)**, exactly as
|
|
182
|
+
* {@link module:engine/generate.itemPackJsonDirs} scopes the local half. The
|
|
183
|
+
* two halves answer the same lookup — `loadItemsMap` merges them into one
|
|
184
|
+
* address space keyed by `subType:shortcode` — so scoping only the local one
|
|
185
|
+
* leaves the collision it was meant to remove: `skill:awar` is a real address
|
|
186
|
+
* in both vocabularies, and a `harn-ensemble` actor compiled for `hm3` would
|
|
187
|
+
* resolve three quarters of its references against whichever document the
|
|
188
|
+
* dependency's `sohl` pack happened to supply. A pack that declares no system
|
|
189
|
+
* is neutral and always read; asking for no system reads every pack, which is
|
|
190
|
+
* every single-system build.
|
|
191
|
+
*
|
|
162
192
|
* @param {object} config - The resolved build configuration.
|
|
163
|
-
* @
|
|
193
|
+
* @param {string|null} [system] - The system the caller is compiling for.
|
|
194
|
+
* Omitted or `null`, every cached pack is read.
|
|
195
|
+
* @returns {Array<{dir: string, package: string}>} Every cached dependency's
|
|
196
|
+
* item directories, each with the package that published it.
|
|
164
197
|
*/
|
|
165
|
-
export function foreignItemCatalogDirs(config: object):
|
|
198
|
+
export function foreignItemCatalogDirs(config: object, system?: string | null): Array<{
|
|
199
|
+
dir: string;
|
|
200
|
+
package: string;
|
|
201
|
+
}>;
|
|
166
202
|
/**
|
|
167
203
|
* The file a system publishes its `system` field sets as (#60).
|
|
168
204
|
*
|
|
@@ -29,6 +29,12 @@ export function anchorsOf(entryUuid: string, entryId: string, body: string, name
|
|
|
29
29
|
* @param {string} name - The note's display name.
|
|
30
30
|
* @param {string} address - The note's package-relative address.
|
|
31
31
|
* @param {string} body - The note's markdown body.
|
|
32
|
+
* **Each entry carries the `id` of the document it addresses**, not only its
|
|
33
|
+
* UUID. The two are one fact — a UUID ends in the id — but only the entry knows
|
|
34
|
+
* which derivation produced it: an item's is its note's `fm.id`, and its
|
|
35
|
+
* documentation journal's is {@link itemDocEntryId} of that. Stating it here is
|
|
36
|
+
* what lets the content index publish an identity it did not re-derive (#310).
|
|
37
|
+
*
|
|
32
38
|
* @param {object} ctx - Resolved identities: `{ contentPackage,
|
|
33
39
|
* foundryPackageId, packRouter }`.
|
|
34
40
|
* @returns {Array<object>} One or two entries, in {@link buildManifest}'s shape.
|