@wildwinter/toolkit 0.2.6 → 0.3.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.
@@ -14,13 +14,11 @@ var MANIFEST_FILE_SUFFIX = ".extension.json";
14
14
  var HOSTS = {
15
15
  patter: {
16
16
  kinds: ["scene", "block", "snippet", "line", "text", "gameEvent", "option", "project"],
17
- references: ["cast", "scene", "block", "snippet", "line"],
18
- claimable: ["scene", "block", "snippet", "line", "text", "gameEvent", "option"]
17
+ references: ["cast", "scene", "block", "snippet", "line"]
19
18
  },
20
19
  storylets: {
21
20
  kinds: ["box", "tagGroup", "tag", "deck", "card", "outcome", "handTemplate", "hand", "project"],
22
- references: ["box", "deck", "card", "outcome", "tag"],
23
- claimable: ["card", "outcome"]
21
+ references: ["box", "deck", "card", "outcome", "tag"]
24
22
  }
25
23
  };
26
24
  var SCALAR_TYPES = ["text", "multiline", "number", "boolean", "enum", "flags", "quality"];
@@ -368,9 +366,14 @@ var VERSION = /^\d+\.\d+\.\d+$/;
368
366
  var NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
369
367
  var TYPE_KEYS = ["type", "values", "stages", "renamedStages", "to", "from", "fields", "key", "of", "id", "min", "max", "valueHelp", "default"];
370
368
  var FIELD_KEYS = /* @__PURE__ */ new Set([...TYPE_KEYS, "name", "label", "help", "required", "group", "editorOnly"]);
371
- var CLAIM_KEYS = /* @__PURE__ */ new Set([...TYPE_KEYS, "setting", "label", "help", "required"]);
372
369
  var BARE_KEYS = new Set(TYPE_KEYS);
373
- var MANIFEST_KEYS = /* @__PURE__ */ new Set(["schema", "id", "name", "version", "docs", "settings", "targets", "views", "reads"]);
370
+ var MANIFEST_KEYS = /* @__PURE__ */ new Set(["schema", "id", "name", "version", "docs", "targets", "views", "reads"]);
371
+ var REMOVED_MANIFEST_KEYS = {
372
+ settings: "settings were removed with claims; declare the extension's own fields, or a project field for a project-wide value"
373
+ };
374
+ var REMOVED_TARGET_KEYS = {
375
+ claims: "claims on host fields were removed; declare a field of the extension's own, which the runtimes hand the game"
376
+ };
374
377
  var VIEW_KEYS = /* @__PURE__ */ new Set(["id", "name", "entry", "element", "hosts"]);
375
378
  var ELEMENT = /^[a-z][a-z0-9._]*-[a-z0-9._-]*$/;
376
379
  var err2 = (code, path, message) => ({ severity: "error", code, path, message });
@@ -381,7 +384,7 @@ function manifestProblems(manifest) {
381
384
  const out = [];
382
385
  if (!isObject(manifest)) return [err2("not-an-object", "", "a manifest is a JSON object")];
383
386
  const m = manifest;
384
- unknownKeys(m, MANIFEST_KEYS, "", out);
387
+ unknownKeys(m, MANIFEST_KEYS, "", out, REMOVED_MANIFEST_KEYS);
385
388
  if (m["schema"] !== MANIFEST_SCHEMA) {
386
389
  out.push(err2("schema", "schema", `the schema tag is ${JSON.stringify(m["schema"])}, not "${MANIFEST_SCHEMA}"`));
387
390
  }
@@ -393,7 +396,6 @@ function manifestProblems(manifest) {
393
396
  out.push(err2("version", "version", "the version is major.minor.patch, such as 1.0.0"));
394
397
  }
395
398
  if (m["docs"] !== void 0 && typeof m["docs"] !== "string") out.push(err2("docs", "docs", "docs is a link, as text"));
396
- const settings = settingsProblems(m["settings"], out);
397
399
  viewsProblems(m["views"], m["targets"], out);
398
400
  readsProblems(m["reads"], m["id"], out);
399
401
  const targets = m["targets"];
@@ -418,12 +420,16 @@ function manifestProblems(manifest) {
418
420
  out.push(err2("unknown-kind", kindPath, `"${kind}" is not a node kind of ${host}; it has ${spec.kinds.join(", ")}`));
419
421
  continue;
420
422
  }
421
- targetProblems(host, kind, target, kindPath, settings, out);
423
+ targetProblems(host, target, kindPath, out);
422
424
  }
423
425
  pickListProblems(kinds, hostPath, out);
424
426
  }
425
427
  return out;
426
428
  }
429
+ function pickWords(from) {
430
+ if (!Array.isArray(from)) return [];
431
+ return from.flatMap((s) => isObject(s) && Array.isArray(s["words"]) ? s["words"].filter((w) => typeof w === "string") : []);
432
+ }
427
433
  function pickSourcesSound(host, from, path, out) {
428
434
  if (!Array.isArray(from) || from.length === 0) {
429
435
  out.push(err2("pick-from", path, 'a pick lists its sources in "from": { "words": [...] }, { "to": a node kind }, or { "list": a project list }'));
@@ -560,81 +566,23 @@ function viewsFor(manifest, host) {
560
566
  const targeted = Object.keys(manifest.targets ?? {});
561
567
  return (Array.isArray(manifest.views) ? manifest.views : []).filter((v) => isObject(v) && typeof v.id === "string" && ID.test(v.id) && typeof v.name === "string" && isViewEntry(v.entry) && typeof v.element === "string" && ELEMENT.test(v.element) && (v.hosts === void 0 ? targeted.includes(host) : Array.isArray(v.hosts) && v.hosts.includes(host)));
562
568
  }
563
- function unknownKeys(obj, known, path, out) {
569
+ function unknownKeys(obj, known, path, out, removed = {}) {
564
570
  for (const k of Object.keys(obj)) {
565
- if (!known.has(k)) {
571
+ if (own(removed, k)) {
572
+ out.push({ severity: "warning", code: "removed-key", path: atKey(path, k), message: `"${k}" is ignored: ${removed[k]}` });
573
+ } else if (!known.has(k)) {
566
574
  out.push({ severity: "warning", code: "unknown-key", path: atKey(path, k), message: `"${k}" means nothing here; it is ignored` });
567
575
  }
568
576
  }
569
577
  }
570
- function settingsProblems(settings, out) {
571
- const names = /* @__PURE__ */ new Set();
572
- if (settings === void 0) return names;
573
- if (!Array.isArray(settings)) {
574
- out.push(err2("not-a-list", "settings", "settings are a list"));
575
- return names;
576
- }
577
- settings.forEach((s, i) => {
578
- const at = atIndex("settings", i);
579
- if (!isObject(s)) {
580
- out.push(err2("not-an-object", at, "a setting is an object"));
581
- return;
582
- }
583
- const name = s["name"];
584
- if (typeof name !== "string" || !NAME.test(name)) {
585
- out.push(err2("setting-name", atKey(at, "name"), "a setting's name is an identifier"));
586
- } else if (names.has(name)) {
587
- out.push(err2("setting-duplicate", atKey(at, "name"), `setting "${name}" is declared twice`));
588
- } else {
589
- names.add(name);
590
- }
591
- const dflt = s.default;
592
- if (typeof dflt !== "string" || !NAME.test(dflt)) {
593
- out.push(err2("setting-default", atKey(at, "default"), "a setting names a host field by default, so its default is a field name"));
594
- }
595
- });
596
- return names;
597
- }
598
- function targetProblems(host, kind, target, path, settings, out) {
578
+ function targetProblems(host, target, path, out) {
599
579
  if (!isObject(target)) {
600
580
  out.push(err2("not-an-object", path, "a target is an object"));
601
581
  return;
602
582
  }
603
- unknownKeys(target, /* @__PURE__ */ new Set(["fields", "claims"]), path, out);
583
+ unknownKeys(target, /* @__PURE__ */ new Set(["fields"]), path, out, REMOVED_TARGET_KEYS);
604
584
  const t = target;
605
585
  if (t.fields !== void 0) fieldListProblems(host, t.fields, atKey(path, "fields"), true, out);
606
- if (t.claims === void 0) return;
607
- if (!Array.isArray(t.claims)) {
608
- out.push(err2("not-a-list", atKey(path, "claims"), "claims are a list"));
609
- return;
610
- }
611
- const claimable = HOSTS[host].claimable.includes(kind);
612
- const claimed = /* @__PURE__ */ new Set();
613
- t.claims.forEach((c, i) => {
614
- const at = atIndex(atKey(path, "claims"), i);
615
- if (!claimable) {
616
- out.push(err2("claim-kind", at, `a ${kind} has no host fields to claim; ${host} has them on ${HOSTS[host].claimable.join(", ")}`));
617
- return;
618
- }
619
- if (!isObject(c)) {
620
- out.push(err2("not-an-object", at, "a claim is an object"));
621
- return;
622
- }
623
- const claim = c;
624
- unknownKeys(c, CLAIM_KEYS, at, out);
625
- if (!SCALAR_TYPES.includes(claim.type)) {
626
- out.push(err2("claim-type", atKey(at, "type"), "a claim presents a host field, which is a scalar"));
627
- } else {
628
- typeProblems(host, claim, at, false, out, true);
629
- }
630
- if (typeof claim.setting !== "string" || !settings.has(claim.setting)) {
631
- out.push(err2("claim-setting", atKey(at, "setting"), `a claim names its field through a setting, and "${String(claim.setting)}" is not one of the manifest's settings`));
632
- } else if (claimed.has(claim.setting)) {
633
- out.push(err2("claim-duplicate", atKey(at, "setting"), `setting "${claim.setting}" is claimed twice for one kind`));
634
- } else {
635
- claimed.add(claim.setting);
636
- }
637
- });
638
586
  }
639
587
  function fieldListProblems(host, fields, path, topLevel, out) {
640
588
  if (!Array.isArray(fields)) {
@@ -668,7 +616,7 @@ function fieldListProblems(host, fields, path, topLevel, out) {
668
616
  }
669
617
  });
670
618
  }
671
- function typeProblems(host, decl, path, named, out, claim = false) {
619
+ function typeProblems(host, decl, path, named, out) {
672
620
  const typeAt = atKey(path, "type");
673
621
  if (decl.type === "string") {
674
622
  out.push(err2("string-type", typeAt, 'the text type is spelled "text"'));
@@ -684,7 +632,6 @@ function typeProblems(host, decl, path, named, out, claim = false) {
684
632
  case "enum":
685
633
  case "flags": {
686
634
  const values = decl.values;
687
- if (claim && values === void 0) break;
688
635
  if (!Array.isArray(values) || values.length === 0) {
689
636
  out.push(err2("values-empty", at("values"), `an ${decl.type === "enum" ? "enum" : "flags field"} lists its values`));
690
637
  sound = false;
@@ -703,7 +650,6 @@ function typeProblems(host, decl, path, named, out, claim = false) {
703
650
  break;
704
651
  }
705
652
  case "quality": {
706
- if (claim && decl.stages === void 0) break;
707
653
  for (const p of expr.qualityDeclarationProblems(decl.stages)) {
708
654
  out.push(err2(
709
655
  `quality-${p.kind === "default-not-a-stage" ? "default" : p.kind}`,
@@ -766,17 +712,15 @@ function typeProblems(host, decl, path, named, out, claim = false) {
766
712
  }
767
713
  }
768
714
  if (decl.renamedStages !== void 0) renamedStagesProblems(decl, at("renamedStages"), out);
769
- if (decl.valueHelp !== void 0 && claim) {
770
- if (!isObject(decl.valueHelp) || !["enum", "flags", "quality"].includes(decl.type)) {
771
- out.push(err2("value-help", at("valueHelp"), "value help describes the values of an enum or flags field, or a quality's stages"));
772
- }
773
- } else if (decl.valueHelp !== void 0) {
774
- const vocabulary = decl.type === "quality" ? decl.stages : decl.type === "enum" || decl.type === "flags" ? decl.values : void 0;
715
+ if (decl.valueHelp !== void 0) {
716
+ const vocabulary = decl.type === "quality" ? decl.stages : decl.type === "enum" || decl.type === "flags" ? decl.values : decl.type === "pick" ? pickWords(decl.from) : void 0;
775
717
  if (vocabulary === void 0 || !isObject(decl.valueHelp)) {
776
- out.push(err2("value-help", at("valueHelp"), "value help describes the values of an enum or flags field, or a quality's stages"));
718
+ out.push(err2("value-help", at("valueHelp"), "value help describes the values of an enum or flags field, a quality's stages, or a pick's words"));
777
719
  } else {
778
720
  for (const k of Object.keys(decl.valueHelp)) {
779
- if (!vocabulary.includes(k)) out.push(err2("value-help", atKey(at("valueHelp"), k), `"${k}" is not one of its values`));
721
+ if (!vocabulary.includes(k)) {
722
+ out.push(err2("value-help", atKey(at("valueHelp"), k), decl.type === "pick" ? `"${k}" is not one of its words: value help describes a pick's fixed words, never the project's lists or nodes` : `"${k}" is not one of its values`));
723
+ }
780
724
  }
781
725
  }
782
726
  }
@@ -809,56 +753,10 @@ function renamedStagesProblems(decl, path, out) {
809
753
  }
810
754
  }
811
755
  }
812
- function settingValues(manifest, chosen) {
813
- const out = {};
814
- for (const s of manifest.settings ?? []) {
815
- const v = chosen !== void 0 && own(chosen, s.name) ? chosen[s.name] : void 0;
816
- out[s.name] = typeof v === "string" && v !== "" ? v : s.default;
817
- }
818
- return out;
819
- }
820
- function resolvedClaims(manifest, host, chosen) {
821
- const settings = settingValues(manifest, chosen);
822
- const out = [];
823
- for (const [kind, target] of Object.entries(manifest.targets[host] ?? {})) {
824
- for (const claim of target.claims ?? []) {
825
- const field = settings[claim.setting];
826
- if (field !== void 0) out.push({ extension: manifest.id, host, kind, field, claim });
827
- }
828
- }
829
- return out;
830
- }
831
- function claimClashes(claims) {
832
- const out = [];
833
- const first = /* @__PURE__ */ new Map();
834
- for (const c of claims) {
835
- const key = `${c.host}\0${c.kind}\0${c.field}`;
836
- const prior = first.get(key);
837
- if (prior === void 0) {
838
- first.set(key, c);
839
- continue;
840
- }
841
- if (prior.extension === c.extension) continue;
842
- out.push(err2(
843
- "claim-clash",
844
- `${c.kind}.${c.field}`,
845
- `extensions "${prior.extension}" and "${c.extension}" both claim the ${c.kind} field "${c.field}"`
846
- ));
847
- }
848
- return out;
849
- }
850
756
  function targetOf(manifest, host, kind) {
851
757
  const kinds = manifest?.targets[host];
852
758
  return kinds !== void 0 && own(kinds, kind) ? kinds[kind] : void 0;
853
759
  }
854
- function effectiveClaim(claim, hostDecl) {
855
- return {
856
- ...claim,
857
- ...claim.values === void 0 && hostDecl.values !== void 0 ? { values: hostDecl.values } : {},
858
- ...claim.stages === void 0 && hostDecl.stages !== void 0 ? { stages: hostDecl.stages } : {},
859
- ...claim.default === void 0 && hostDecl.default !== void 0 ? { default: hostDecl.default } : {}
860
- };
861
- }
862
760
 
863
761
  // src/extensions/merge.ts
864
762
  var ATOMIC = { kind: "atomic" };
@@ -1208,11 +1106,9 @@ exports.UNRESOLVED_KEY = UNRESOLVED_KEY;
1208
1106
  exports.VALUE_TYPES = VALUE_TYPES;
1209
1107
  exports.atIndex = atIndex;
1210
1108
  exports.atKey = atKey;
1211
- exports.claimClashes = claimClashes;
1212
1109
  exports.compileEntry = compileEntry;
1213
1110
  exports.compiledTarget = compiledTarget;
1214
1111
  exports.defaultOf = defaultOf;
1215
- exports.effectiveClaim = effectiveClaim;
1216
1112
  exports.entryProblems = entryProblems;
1217
1113
  exports.extensionDataStrategy = extensionDataStrategy;
1218
1114
  exports.isTextList = isTextList;
@@ -1231,8 +1127,6 @@ exports.pickChoices = pickChoices;
1231
1127
  exports.pickClashes = pickClashes;
1232
1128
  exports.pickLists = pickLists;
1233
1129
  exports.renameStages = renameStages;
1234
- exports.resolvedClaims = resolvedClaims;
1235
- exports.settingValues = settingValues;
1236
1130
  exports.stableStringify = stableStringify;
1237
1131
  exports.strategyForTarget = strategyForTarget;
1238
1132
  exports.strategyForType = strategyForType;