@heroiclands/package-build 22.0.1 → 22.0.2
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 +10 -0
- package/docs/api.md +8 -7
- package/docs/content-format.md +11 -0
- package/engine/asset-index.mjs +44 -0
- package/package.json +1 -1
- package/types/engine/asset-index.d.mts +20 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 22.0.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 83fa58e: **A record states `attribution` and `license`, or it is a finding.** The nearest
|
|
8
|
+
provenance record is the whole answer and is never merged with the one above it,
|
|
9
|
+
so a key left out is not inherited — it is absent, and a record stating neither
|
|
10
|
+
would leave every file beneath it with no rights holder and no terms. `source`,
|
|
11
|
+
`ai` and `notes` stay optional, because blank is a truthful answer for each.
|
|
12
|
+
|
|
3
13
|
## 22.0.1
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
package/docs/api.md
CHANGED
|
@@ -567,13 +567,14 @@ The asset types — `icon`, `image` and `audio` — and the three roots they are
|
|
|
567
567
|
|
|
568
568
|
The asset record: one line of the content index per addressable file. The record is emitted by the package holding the bytes, so its `path` is that package's path and each consumer joins its own root onto it — Foundry its data directory, the website its CDN prefix, the book its asset base.
|
|
569
569
|
|
|
570
|
-
| Export | Signature | Returns | Use it when
|
|
571
|
-
| --------------------------- | --------------------------------------------------------------- | ----------------------------- |
|
|
572
|
-
| `ASSET_RECORD_FIELDS` | `const ASSET_RECORD_FIELDS` | `readonly AssetRecordField[]` | reading what an `asset` block carries, and where each field comes from
|
|
573
|
-
| `PROVENANCE_KEYS` | `const PROVENANCE_KEYS` | `ReadonlySet<string>` | validating a provenance file, whose keys are derived from the record's own fields
|
|
574
|
-
| `
|
|
575
|
-
| `
|
|
576
|
-
| `
|
|
570
|
+
| Export | Signature | Returns | Use it when |
|
|
571
|
+
| --------------------------- | --------------------------------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
572
|
+
| `ASSET_RECORD_FIELDS` | `const ASSET_RECORD_FIELDS` | `readonly AssetRecordField[]` | reading what an `asset` block carries, and where each field comes from |
|
|
573
|
+
| `PROVENANCE_KEYS` | `const PROVENANCE_KEYS` | `ReadonlySet<string>` | validating a provenance file, whose keys are derived from the record's own fields |
|
|
574
|
+
| `REQUIRED_PROVENANCE_KEYS` | `const REQUIRED_PROVENANCE_KEYS` | `ReadonlySet<string>` | the keys a provenance file must state — a record resolves wholesale, so one omitting them leaves every file it covers with no rights holder or no terms |
|
|
575
|
+
| `PROVENANCE_FILE` | `const PROVENANCE_FILE` | `string` — `provenance.yaml` | naming the file a directory records provenance for its subtree in |
|
|
576
|
+
| `PROVENANCE_SIDECAR_SUFFIX` | `const PROVENANCE_SIDECAR_SUFFIX` | `string` — `.yaml` | naming a per-file record, which replaces an inherited one wholesale |
|
|
577
|
+
| `collectAssetRecords` | `collectAssetRecords(assetsBase, { contentPackage, problems })` | `Array<Record<string, any>>` | reading a package's asset roots into index records, without walking its notes |
|
|
577
578
|
|
|
578
579
|
### `engine.packages`
|
|
579
580
|
|
package/docs/content-format.md
CHANGED
|
@@ -1508,6 +1508,17 @@ One address resolves its record in this order:
|
|
|
1508
1508
|
3. Otherwise nothing, and the provenance fields are blank. A package that
|
|
1509
1509
|
records no attribution is a fact to state, not a walk to fail.
|
|
1510
1510
|
|
|
1511
|
+
**The nearest record is the whole answer.** It is never merged with the one
|
|
1512
|
+
above it, so a record states every key it means to claim — which is what lets a
|
|
1513
|
+
reader open one file and know what the tree beneath it carries, instead of
|
|
1514
|
+
resolving a chain by hand.
|
|
1515
|
+
|
|
1516
|
+
That is also why `attribution` and `license` are **required in any record that
|
|
1517
|
+
exists**: a key left out is not inherited from above, it is simply absent, so a
|
|
1518
|
+
record stating neither leaves every file it covers with no rights holder and no
|
|
1519
|
+
terms. Omitting one is a finding. `source`, `ai` and `notes` stay optional,
|
|
1520
|
+
because a blank is a truthful answer for each of them.
|
|
1521
|
+
|
|
1511
1522
|
#### A font is not an asset
|
|
1512
1523
|
|
|
1513
1524
|
`assets/fonts` is not a root, and a font has no address.
|
package/engine/asset-index.mjs
CHANGED
|
@@ -88,6 +88,10 @@ export const PROVENANCE_SIDECAR_SUFFIX = ".yaml";
|
|
|
88
88
|
* @property {string} name - The key inside `asset`.
|
|
89
89
|
* @property {"walk"|"provenance"} from - Where the value comes from: the walk
|
|
90
90
|
* itself, or the provenance record resolved for the address.
|
|
91
|
+
* @property {boolean} [required] - Whether a provenance record that exists must
|
|
92
|
+
* state this key. Omitting one is a finding rather than a blank, because a
|
|
93
|
+
* record resolves wholesale: the nearest one is the whole answer, so a key it
|
|
94
|
+
* leaves out is not inherited from above but simply absent.
|
|
91
95
|
* @property {string} describe - One line, for the author-facing reference.
|
|
92
96
|
*/
|
|
93
97
|
|
|
@@ -119,6 +123,7 @@ export const ASSET_RECORD_FIELDS = Object.freeze([
|
|
|
119
123
|
Object.freeze({
|
|
120
124
|
name: "attribution",
|
|
121
125
|
from: "provenance",
|
|
126
|
+
required: true,
|
|
122
127
|
describe: "The person holding the rights, to whom attribution is legally due.",
|
|
123
128
|
}),
|
|
124
129
|
Object.freeze({
|
|
@@ -134,6 +139,7 @@ export const ASSET_RECORD_FIELDS = Object.freeze([
|
|
|
134
139
|
Object.freeze({
|
|
135
140
|
name: "license",
|
|
136
141
|
from: "provenance",
|
|
142
|
+
required: true,
|
|
137
143
|
describe: "The licence it is used under — an SPDX identifier, or terms.",
|
|
138
144
|
}),
|
|
139
145
|
Object.freeze({
|
|
@@ -159,6 +165,22 @@ export const PROVENANCE_KEYS = Object.freeze(
|
|
|
159
165
|
),
|
|
160
166
|
);
|
|
161
167
|
|
|
168
|
+
/**
|
|
169
|
+
* The keys a provenance file must state.
|
|
170
|
+
*
|
|
171
|
+
* Derived from {@link ASSET_RECORD_FIELDS} for the same reason the key set is:
|
|
172
|
+
* the rule and the record cannot disagree about which keys are required.
|
|
173
|
+
*
|
|
174
|
+
* @type {ReadonlySet<string>}
|
|
175
|
+
*/
|
|
176
|
+
export const REQUIRED_PROVENANCE_KEYS = Object.freeze(
|
|
177
|
+
new Set(
|
|
178
|
+
ASSET_RECORD_FIELDS.filter((field) => field.from === "provenance" && field.required).map(
|
|
179
|
+
(field) => field.name,
|
|
180
|
+
),
|
|
181
|
+
),
|
|
182
|
+
);
|
|
183
|
+
|
|
162
184
|
/**
|
|
163
185
|
* Read one provenance file, reporting every key that is not a provenance key.
|
|
164
186
|
*
|
|
@@ -218,6 +240,25 @@ function readProvenanceFile(file, findings) {
|
|
|
218
240
|
}
|
|
219
241
|
out[key] = value == null ? "" : String(value);
|
|
220
242
|
}
|
|
243
|
+
|
|
244
|
+
// A record resolves wholesale — the nearest one is the whole answer, and no
|
|
245
|
+
// ancestor fills a key it leaves out. So an omitted `attribution` or
|
|
246
|
+
// `license` is not a value inherited from above; it is a file with no stated
|
|
247
|
+
// rights holder and no stated terms, which is the one thing a provenance
|
|
248
|
+
// record exists to prevent. Reported against the file rather than a line,
|
|
249
|
+
// because the fault is an absence and has no position.
|
|
250
|
+
for (const key of REQUIRED_PROVENANCE_KEYS) {
|
|
251
|
+
if (out[key]) continue;
|
|
252
|
+
findings.push({
|
|
253
|
+
file,
|
|
254
|
+
severity: "error",
|
|
255
|
+
message:
|
|
256
|
+
`a provenance record states \`${key}\`, and this one does not. ` +
|
|
257
|
+
"A record replaces rather than extends the one above it, so every " +
|
|
258
|
+
"file it covers would carry no " +
|
|
259
|
+
(key === "license" ? "licence" : key),
|
|
260
|
+
});
|
|
261
|
+
}
|
|
221
262
|
return out;
|
|
222
263
|
}
|
|
223
264
|
|
|
@@ -239,6 +280,9 @@ function inheritedProvenance(dir, root, cache, findings) {
|
|
|
239
280
|
const own = path.join(dir, PROVENANCE_FILE);
|
|
240
281
|
let answer;
|
|
241
282
|
if (fs.existsSync(own)) {
|
|
283
|
+
// The nearest record is the whole answer. It is not merged with the one
|
|
284
|
+
// above it, so a record states every key it means to claim and a reader
|
|
285
|
+
// needs only the file in front of them to know what a file carries.
|
|
242
286
|
answer = readProvenanceFile(own, findings);
|
|
243
287
|
} else if (path.resolve(dir) === path.resolve(root)) {
|
|
244
288
|
// The search stops at the type root: `assets/` above it is the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "22.0.
|
|
3
|
+
"version": "22.0.2",
|
|
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",
|
|
@@ -43,6 +43,10 @@ export const PROVENANCE_SIDECAR_SUFFIX: string;
|
|
|
43
43
|
* @property {string} name - The key inside `asset`.
|
|
44
44
|
* @property {"walk"|"provenance"} from - Where the value comes from: the walk
|
|
45
45
|
* itself, or the provenance record resolved for the address.
|
|
46
|
+
* @property {boolean} [required] - Whether a provenance record that exists must
|
|
47
|
+
* state this key. Omitting one is a finding rather than a blank, because a
|
|
48
|
+
* record resolves wholesale: the nearest one is the whole answer, so a key it
|
|
49
|
+
* leaves out is not inherited from above but simply absent.
|
|
46
50
|
* @property {string} describe - One line, for the author-facing reference.
|
|
47
51
|
*/
|
|
48
52
|
/**
|
|
@@ -72,6 +76,15 @@ export const ASSET_RECORD_FIELDS: readonly AssetRecordField[];
|
|
|
72
76
|
* @type {ReadonlySet<string>}
|
|
73
77
|
*/
|
|
74
78
|
export const PROVENANCE_KEYS: ReadonlySet<string>;
|
|
79
|
+
/**
|
|
80
|
+
* The keys a provenance file must state.
|
|
81
|
+
*
|
|
82
|
+
* Derived from {@link ASSET_RECORD_FIELDS} for the same reason the key set is:
|
|
83
|
+
* the rule and the record cannot disagree about which keys are required.
|
|
84
|
+
*
|
|
85
|
+
* @type {ReadonlySet<string>}
|
|
86
|
+
*/
|
|
87
|
+
export const REQUIRED_PROVENANCE_KEYS: ReadonlySet<string>;
|
|
75
88
|
/**
|
|
76
89
|
* One field of the `asset` block.
|
|
77
90
|
*/
|
|
@@ -85,6 +98,13 @@ export type AssetRecordField = {
|
|
|
85
98
|
* itself, or the provenance record resolved for the address.
|
|
86
99
|
*/
|
|
87
100
|
from: "walk" | "provenance";
|
|
101
|
+
/**
|
|
102
|
+
* - Whether a provenance record that exists must
|
|
103
|
+
* state this key. Omitting one is a finding rather than a blank, because a
|
|
104
|
+
* record resolves wholesale: the nearest one is the whole answer, so a key it
|
|
105
|
+
* leaves out is not inherited from above but simply absent.
|
|
106
|
+
*/
|
|
107
|
+
required?: boolean | undefined;
|
|
88
108
|
/**
|
|
89
109
|
* - One line, for the author-facing reference.
|
|
90
110
|
*/
|