@rpgm-tools/neo-angband-mod-sdk 1.3.0 → 1.9.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/README.md +31 -1
- package/bin/neo-angband-mod-check.mjs +2 -0
- package/dist/capabilities.d.ts +23 -1
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +32 -3
- package/dist/capabilities.js.map +1 -1
- package/dist/manifest.d.ts +16 -2
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +24 -0
- package/dist/manifest.js.map +1 -1
- package/dist/standards.d.ts +7 -4
- package/dist/standards.d.ts.map +1 -1
- package/dist/standards.js +43 -5
- package/dist/standards.js.map +1 -1
- package/docs/AUTHORING.md +10 -8
- package/docs/MOD_COMPATIBILITY.md +74 -17
- package/docs/MOD_LIFECYCLE.md +68 -31
- package/docs/MOD_REACH.md +19 -2
- package/docs/MOD_SEAMS.md +7 -0
- package/docs/PLUGINS.md +34 -4
- package/docs/README.md +11 -8
- package/docs/REQUIREMENTS.md +6 -0
- package/docs/tutorials/01-tweak-a-value.md +4 -4
- package/docs/tutorials/02-add-an-item.md +21 -21
- package/docs/tutorials/03-add-a-monster.md +23 -23
- package/docs/tutorials/04-change-a-spell.md +5 -5
- package/docs/tutorials/05-hook-behaviour.md +8 -8
- package/docs/tutorials/06-add-an-option.md +7 -7
- package/docs/tutorials/07-add-an-artifact.md +13 -13
- package/docs/tutorials/README.md +21 -18
- package/package.json +1 -1
- package/src/capabilities.ts +35 -5
- package/src/manifest.ts +43 -2
- package/src/standards.ts +41 -5
package/src/manifest.ts
CHANGED
|
@@ -304,11 +304,16 @@ export const DEFAULT_PACK_GROUP = "content";
|
|
|
304
304
|
* runtime grants only what a `shape: plugin` pack declares and the user
|
|
305
305
|
* approves; content and tile packs request none. The vocabulary
|
|
306
306
|
* ("command:add", "event:turn-start", "state:*.read", "network:<host>", ...)
|
|
307
|
-
* is enforced by
|
|
308
|
-
*
|
|
307
|
+
* is enforced by parseCapability / CapabilitySet.fromManifest in
|
|
308
|
+
* capabilities.ts. The manifest records the request as strings; checkMod
|
|
309
|
+
* runs that same function, so an unrecognized capability is a check
|
|
310
|
+
* failure rather than a silent accept.
|
|
309
311
|
*/
|
|
310
312
|
export type Capability = string;
|
|
311
313
|
|
|
314
|
+
/** How code declared by a plugin-facet pack is executed. */
|
|
315
|
+
export type PluginRuntime = "in-process" | "worker";
|
|
316
|
+
|
|
312
317
|
/**
|
|
313
318
|
* One graphics mode a `tiles`-facet pack contributes.
|
|
314
319
|
*
|
|
@@ -479,6 +484,16 @@ export interface PackManifest {
|
|
|
479
484
|
* exist yet.
|
|
480
485
|
*/
|
|
481
486
|
modApi?: number;
|
|
487
|
+
/**
|
|
488
|
+
* The execution ABI selected by a code pack. API 1 keeps the historical
|
|
489
|
+
* in-process `plugin.js` entry; API 2 requires the isolated worker runtime.
|
|
490
|
+
*/
|
|
491
|
+
runtime?: PluginRuntime;
|
|
492
|
+
/**
|
|
493
|
+
* The module imported by the host-owned API-2 Worker bootstrap. This is a
|
|
494
|
+
* mod-relative file, never a host module name or URL.
|
|
495
|
+
*/
|
|
496
|
+
workerEntry?: string;
|
|
482
497
|
/**
|
|
483
498
|
* Player-toggleable flags this pack owns (see PackRule). The bundled qol /
|
|
484
499
|
* bug-fixes mods use this to declare their fixes/tweaks for the in-app "Fixes
|
|
@@ -683,6 +698,32 @@ export function validateManifest(value: unknown): PackManifest {
|
|
|
683
698
|
);
|
|
684
699
|
}
|
|
685
700
|
}
|
|
701
|
+
if (m["runtime"] !== undefined && m["runtime"] !== "in-process" && m["runtime"] !== "worker") {
|
|
702
|
+
throw new ManifestError(`manifest ${id}: runtime must be "in-process" or "worker"`);
|
|
703
|
+
}
|
|
704
|
+
if (m["workerEntry"] !== undefined) {
|
|
705
|
+
const entry = m["workerEntry"];
|
|
706
|
+
if (
|
|
707
|
+
typeof entry !== "string" ||
|
|
708
|
+
entry.length === 0 ||
|
|
709
|
+
entry.startsWith("/") ||
|
|
710
|
+
entry.includes("\\") ||
|
|
711
|
+
entry.split("/").some((part) => part === "" || part === "." || part === "..")
|
|
712
|
+
) {
|
|
713
|
+
throw new ManifestError(`manifest ${id}: workerEntry must be a non-empty relative file path`);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
if (m["modApi"] === 2) {
|
|
717
|
+
if (m["runtime"] !== "worker") {
|
|
718
|
+
throw new ManifestError(`manifest ${id}: modApi 2 requires runtime "worker"`);
|
|
719
|
+
}
|
|
720
|
+
if (typeof m["workerEntry"] !== "string") {
|
|
721
|
+
throw new ManifestError(`manifest ${id}: modApi 2 requires workerEntry`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
if (m["runtime"] === "worker" && m["modApi"] !== 2) {
|
|
725
|
+
throw new ManifestError(`manifest ${id}: runtime "worker" requires modApi 2`);
|
|
726
|
+
}
|
|
686
727
|
if (
|
|
687
728
|
m["nondeterministic"] !== undefined &&
|
|
688
729
|
typeof m["nondeterministic"] !== "boolean"
|
package/src/standards.ts
CHANGED
|
@@ -15,10 +15,13 @@
|
|
|
15
15
|
* cannot describe a rule that is not enforced or omit one that is.
|
|
16
16
|
*
|
|
17
17
|
* WHAT IT DOES NOT DUPLICATE. Everything validateManifest already decides is
|
|
18
|
-
* delegated to validateManifest.
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* delegated to validateManifest. Capability grammar is the same: this module
|
|
19
|
+
* calls CapabilitySet.fromManifest rather than restating parseCapability's
|
|
20
|
+
* patterns. What it adds is the rules that need to see something those two
|
|
21
|
+
* cannot: the FILE LIST, and the join between a validated manifest and the
|
|
22
|
+
* capability model (validateManifest cannot import capabilities.ts without a
|
|
23
|
+
* cycle). Two of the file-list rules below are defects this project actually
|
|
24
|
+
* shipped, both invisible to a validator with no files to look at:
|
|
22
25
|
*
|
|
23
26
|
* - `modApi` is documented as REQUIRED of any pack shipping plugin.js, and nothing
|
|
24
27
|
* enforced it, because validateManifest never sees whether plugin.js is there.
|
|
@@ -32,8 +35,9 @@
|
|
|
32
35
|
* learn to ignore.
|
|
33
36
|
*/
|
|
34
37
|
|
|
35
|
-
import {
|
|
38
|
+
import { CapabilitySet } from "./capabilities.js";
|
|
36
39
|
import { ManifestError, hasFacet, validateManifest, type PackManifest } from "./manifest.js";
|
|
40
|
+
import { compareSemver, satisfies } from "./semver.js";
|
|
37
41
|
|
|
38
42
|
/** The file a mod's code lives in, when it has any. */
|
|
39
43
|
export const PLUGIN_FILE = "plugin.js";
|
|
@@ -299,6 +303,38 @@ export const MOD_REQUIREMENTS: readonly Requirement[] = [
|
|
|
299
303
|
}
|
|
300
304
|
},
|
|
301
305
|
},
|
|
306
|
+
{
|
|
307
|
+
id: "capabilities-recognized",
|
|
308
|
+
level: "required",
|
|
309
|
+
title: "Request only capabilities the game knows",
|
|
310
|
+
why:
|
|
311
|
+
"A capability string is the consent surface a player reads and the gate the " +
|
|
312
|
+
"runtime actually opens. An unrecognized string, or a capability on a pack " +
|
|
313
|
+
"that cannot execute, is refused when the game loads the plugin - after the " +
|
|
314
|
+
"player has already installed it. This check is CapabilitySet.fromManifest, " +
|
|
315
|
+
"the same function the loader calls, so a typo that used to pass here and " +
|
|
316
|
+
"fail there cannot.",
|
|
317
|
+
check: (mod) => {
|
|
318
|
+
const m = manifestObject(mod);
|
|
319
|
+
if (m === null) return null;
|
|
320
|
+
let manifest: PackManifest;
|
|
321
|
+
try {
|
|
322
|
+
/* validateManifest first, so this rule never re-describes the schema. A
|
|
323
|
+
* malformed capabilities field (not an array of strings) belongs to
|
|
324
|
+
* manifest-fields; this rule only speaks once there is a PackManifest
|
|
325
|
+
* to hand to the capability model. */
|
|
326
|
+
manifest = validateManifest(m);
|
|
327
|
+
} catch {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
try {
|
|
331
|
+
CapabilitySet.fromManifest(manifest);
|
|
332
|
+
return null;
|
|
333
|
+
} catch (e) {
|
|
334
|
+
return e instanceof Error ? e.message : String(e);
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
},
|
|
302
338
|
{
|
|
303
339
|
id: "plugin-declares-modapi",
|
|
304
340
|
level: "required",
|