@wildwinter/toolkit 0.3.0 → 0.3.1

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.
@@ -80,6 +80,9 @@ interface FieldDecl extends TypeDecl {
80
80
  readonly group?: string;
81
81
  /** Stripped at compile: editor data, never in a bundle. A target's own fields only. */
82
82
  readonly editorOnly?: boolean;
83
+ /** One of the manifest's views, by id, that edits this field in the hosts it opens in: a target's own map of
84
+ * records only. */
85
+ readonly editor?: string;
83
86
  }
84
87
  /** What an extension declares for one node kind of one host. */
85
88
  interface TargetDecl {
@@ -96,6 +99,8 @@ interface Manifest {
96
99
  readonly views?: readonly ViewDecl[];
97
100
  /** Other extensions whose data this one's views read, by id. */
98
101
  readonly reads?: readonly string[];
102
+ /** Other extensions a project must take for this one to work, by id. */
103
+ readonly requires?: readonly string[];
99
104
  }
100
105
  /** A view: a web component in the extension's installed package, opened by a host in a floating window. */
101
106
  interface ViewDecl {
@@ -145,6 +150,9 @@ declare function parseManifest(text: string): Manifest;
145
150
  declare function manifestProblems(manifest: unknown): Problem[];
146
151
  /** A package path a view's entry may name: relative, forward slashes, no `.` or `..` segment, a `.js` file. */
147
152
  declare function isViewEntry(entry: unknown): entry is string;
153
+ /** The extensions a manifest requires that a project doesn't take, by id, in the manifest's order. An entry that
154
+ * fails its checks is left out. */
155
+ declare function missingRequirements(manifest: Manifest, taken: Iterable<string>): string[];
148
156
  /** The views a host offers from a manifest: those naming the host, or every view when it names none and the
149
157
  * manifest targets the host. A view that fails its checks is left out. */
150
158
  declare function viewsFor(manifest: Manifest, host: HostName): ViewDecl[];
@@ -372,4 +380,4 @@ declare const atKey: (base: string, key: string) => string;
372
380
  /** `base` and then list position `i`. */
373
381
  declare const atIndex: (base: string, i: number) => string;
374
382
 
375
- export { ATOMIC, COMMENTS, type Conflict, type ConflictKind, type DataMergeResult, type ExtensionData, type FieldDecl, HOSTS, type HostName, type HostSpec, type JsonValue, MANIFEST_FILE_SUFFIX, MANIFEST_SCHEMA, type Manifest, type MergeWarning, type PickChoice, type PickSource, type PickSources, type Problem, SCALAR_TYPES, SET, type ScalarType, type StageRename, type Strategy, type TargetDecl, type TreeMergeOptions, type TreeMergeResult, type TypeDecl, UNRESOLVED_KEY, type UnresolvedEntry, VALUE_TYPES, type ValueContext, type ValueType, type ViewDecl, atIndex, atKey, compileEntry, compiledTarget, defaultOf, entryProblems, extensionDataStrategy, isTextList, isViewEntry, keyedBy, manifestProblems, mapOf, markUnresolved, mergeComments, mergeExtensionData, mergeTree, objectOf, orphanedEntries, parseManifest, pickChoices, pickClashes, pickLists, renameStages, stableStringify, strategyForTarget, strategyForType, targetOf, unresolvedOf, unresolvedProblems, valueProblems, viewsFor, withoutMark };
383
+ export { ATOMIC, COMMENTS, type Conflict, type ConflictKind, type DataMergeResult, type ExtensionData, type FieldDecl, HOSTS, type HostName, type HostSpec, type JsonValue, MANIFEST_FILE_SUFFIX, MANIFEST_SCHEMA, type Manifest, type MergeWarning, type PickChoice, type PickSource, type PickSources, type Problem, SCALAR_TYPES, SET, type ScalarType, type StageRename, type Strategy, type TargetDecl, type TreeMergeOptions, type TreeMergeResult, type TypeDecl, UNRESOLVED_KEY, type UnresolvedEntry, VALUE_TYPES, type ValueContext, type ValueType, type ViewDecl, atIndex, atKey, compileEntry, compiledTarget, defaultOf, entryProblems, extensionDataStrategy, isTextList, isViewEntry, keyedBy, manifestProblems, mapOf, markUnresolved, mergeComments, mergeExtensionData, mergeTree, missingRequirements, objectOf, orphanedEntries, parseManifest, pickChoices, pickClashes, pickLists, renameStages, stableStringify, strategyForTarget, strategyForType, targetOf, unresolvedOf, unresolvedProblems, valueProblems, viewsFor, withoutMark };
@@ -80,6 +80,9 @@ interface FieldDecl extends TypeDecl {
80
80
  readonly group?: string;
81
81
  /** Stripped at compile: editor data, never in a bundle. A target's own fields only. */
82
82
  readonly editorOnly?: boolean;
83
+ /** One of the manifest's views, by id, that edits this field in the hosts it opens in: a target's own map of
84
+ * records only. */
85
+ readonly editor?: string;
83
86
  }
84
87
  /** What an extension declares for one node kind of one host. */
85
88
  interface TargetDecl {
@@ -96,6 +99,8 @@ interface Manifest {
96
99
  readonly views?: readonly ViewDecl[];
97
100
  /** Other extensions whose data this one's views read, by id. */
98
101
  readonly reads?: readonly string[];
102
+ /** Other extensions a project must take for this one to work, by id. */
103
+ readonly requires?: readonly string[];
99
104
  }
100
105
  /** A view: a web component in the extension's installed package, opened by a host in a floating window. */
101
106
  interface ViewDecl {
@@ -145,6 +150,9 @@ declare function parseManifest(text: string): Manifest;
145
150
  declare function manifestProblems(manifest: unknown): Problem[];
146
151
  /** A package path a view's entry may name: relative, forward slashes, no `.` or `..` segment, a `.js` file. */
147
152
  declare function isViewEntry(entry: unknown): entry is string;
153
+ /** The extensions a manifest requires that a project doesn't take, by id, in the manifest's order. An entry that
154
+ * fails its checks is left out. */
155
+ declare function missingRequirements(manifest: Manifest, taken: Iterable<string>): string[];
148
156
  /** The views a host offers from a manifest: those naming the host, or every view when it names none and the
149
157
  * manifest targets the host. A view that fails its checks is left out. */
150
158
  declare function viewsFor(manifest: Manifest, host: HostName): ViewDecl[];
@@ -372,4 +380,4 @@ declare const atKey: (base: string, key: string) => string;
372
380
  /** `base` and then list position `i`. */
373
381
  declare const atIndex: (base: string, i: number) => string;
374
382
 
375
- export { ATOMIC, COMMENTS, type Conflict, type ConflictKind, type DataMergeResult, type ExtensionData, type FieldDecl, HOSTS, type HostName, type HostSpec, type JsonValue, MANIFEST_FILE_SUFFIX, MANIFEST_SCHEMA, type Manifest, type MergeWarning, type PickChoice, type PickSource, type PickSources, type Problem, SCALAR_TYPES, SET, type ScalarType, type StageRename, type Strategy, type TargetDecl, type TreeMergeOptions, type TreeMergeResult, type TypeDecl, UNRESOLVED_KEY, type UnresolvedEntry, VALUE_TYPES, type ValueContext, type ValueType, type ViewDecl, atIndex, atKey, compileEntry, compiledTarget, defaultOf, entryProblems, extensionDataStrategy, isTextList, isViewEntry, keyedBy, manifestProblems, mapOf, markUnresolved, mergeComments, mergeExtensionData, mergeTree, objectOf, orphanedEntries, parseManifest, pickChoices, pickClashes, pickLists, renameStages, stableStringify, strategyForTarget, strategyForType, targetOf, unresolvedOf, unresolvedProblems, valueProblems, viewsFor, withoutMark };
383
+ export { ATOMIC, COMMENTS, type Conflict, type ConflictKind, type DataMergeResult, type ExtensionData, type FieldDecl, HOSTS, type HostName, type HostSpec, type JsonValue, MANIFEST_FILE_SUFFIX, MANIFEST_SCHEMA, type Manifest, type MergeWarning, type PickChoice, type PickSource, type PickSources, type Problem, SCALAR_TYPES, SET, type ScalarType, type StageRename, type Strategy, type TargetDecl, type TreeMergeOptions, type TreeMergeResult, type TypeDecl, UNRESOLVED_KEY, type UnresolvedEntry, VALUE_TYPES, type ValueContext, type ValueType, type ViewDecl, atIndex, atKey, compileEntry, compiledTarget, defaultOf, entryProblems, extensionDataStrategy, isTextList, isViewEntry, keyedBy, manifestProblems, mapOf, markUnresolved, mergeComments, mergeExtensionData, mergeTree, missingRequirements, objectOf, orphanedEntries, parseManifest, pickChoices, pickClashes, pickLists, renameStages, stableStringify, strategyForTarget, strategyForType, targetOf, unresolvedOf, unresolvedProblems, valueProblems, viewsFor, withoutMark };
@@ -355,9 +355,9 @@ var ID = /^[a-z][a-z0-9-]*$/;
355
355
  var VERSION = /^\d+\.\d+\.\d+$/;
356
356
  var NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
357
357
  var TYPE_KEYS = ["type", "values", "stages", "renamedStages", "to", "from", "fields", "key", "of", "id", "min", "max", "valueHelp", "default"];
358
- var FIELD_KEYS = /* @__PURE__ */ new Set([...TYPE_KEYS, "name", "label", "help", "required", "group", "editorOnly"]);
358
+ var FIELD_KEYS = /* @__PURE__ */ new Set([...TYPE_KEYS, "name", "label", "help", "required", "group", "editorOnly", "editor"]);
359
359
  var BARE_KEYS = new Set(TYPE_KEYS);
360
- var MANIFEST_KEYS = /* @__PURE__ */ new Set(["schema", "id", "name", "version", "docs", "targets", "views", "reads"]);
360
+ var MANIFEST_KEYS = /* @__PURE__ */ new Set(["schema", "id", "name", "version", "docs", "targets", "views", "reads", "requires"]);
361
361
  var REMOVED_MANIFEST_KEYS = {
362
362
  settings: "settings were removed with claims; declare the extension's own fields, or a project field for a project-wide value"
363
363
  };
@@ -388,6 +388,7 @@ function manifestProblems(manifest) {
388
388
  if (m["docs"] !== void 0 && typeof m["docs"] !== "string") out.push(err2("docs", "docs", "docs is a link, as text"));
389
389
  viewsProblems(m["views"], m["targets"], out);
390
390
  readsProblems(m["reads"], m["id"], out);
391
+ requiresProblems(m["requires"], m["id"], out);
391
392
  const targets = m["targets"];
392
393
  if (!isObject(targets) || Object.keys(targets).length === 0) {
393
394
  out.push(err2("no-targets", "targets", "the manifest targets no host"));
@@ -413,6 +414,7 @@ function manifestProblems(manifest) {
413
414
  targetProblems(host, target, kindPath, out);
414
415
  }
415
416
  pickListProblems(kinds, hostPath, out);
417
+ editorProblems(host, kinds, m["views"], Object.keys(targets), hostPath, out);
416
418
  }
417
419
  return out;
418
420
  }
@@ -552,6 +554,54 @@ function readsProblems(reads, self, out) {
552
554
  else seen.add(r);
553
555
  });
554
556
  }
557
+ function requiresProblems(requires, self, out) {
558
+ if (requires === void 0) return;
559
+ if (!Array.isArray(requires)) {
560
+ out.push(err2("not-a-list", "requires", "requires is a list of extension ids"));
561
+ return;
562
+ }
563
+ const seen = /* @__PURE__ */ new Set();
564
+ requires.forEach((r, i) => {
565
+ const at = atIndex("requires", i);
566
+ if (typeof r !== "string" || !ID.test(r)) out.push(err2("requires-id", at, "a required extension is named by its id: lower case letters, digits and hyphens"));
567
+ else if (r === self) out.push(err2("requires-self", at, "an extension can't require itself"));
568
+ else if (seen.has(r)) out.push(err2("requires-duplicate", at, `"${r}" is listed twice`));
569
+ else seen.add(r);
570
+ });
571
+ }
572
+ function missingRequirements(manifest, taken) {
573
+ const have = new Set(taken);
574
+ const out = [];
575
+ for (const r of Array.isArray(manifest.requires) ? manifest.requires : []) {
576
+ if (typeof r === "string" && ID.test(r) && r !== manifest.id && !have.has(r) && !out.includes(r)) out.push(r);
577
+ }
578
+ return out;
579
+ }
580
+ function editorProblems(host, kinds, views, targeted, hostPath, out) {
581
+ const declared = Array.isArray(views) ? views.filter((v) => isObject(v) && typeof v["id"] === "string") : [];
582
+ for (const [kind, target] of Object.entries(kinds)) {
583
+ if (!isObject(target) || !Array.isArray(target["fields"])) continue;
584
+ target["fields"].forEach((f, i) => {
585
+ if (!isObject(f) || f["editor"] === void 0) return;
586
+ const at = atKey(atIndex(atKey(atKey(hostPath, kind), "fields"), i), "editor");
587
+ const editor = f["editor"];
588
+ const view = typeof editor === "string" ? declared.find((v) => v["id"] === editor) : void 0;
589
+ if (view === void 0) {
590
+ out.push(err2("editor-view", at, typeof editor === "string" ? `the manifest declares no view called "${editor}"; an editor names one of its views by id` : "an editor names one of the manifest's views by its id"));
591
+ return;
592
+ }
593
+ const opensIn = Array.isArray(view["hosts"]) ? view["hosts"] : targeted;
594
+ if (!opensIn.includes(host)) {
595
+ out.push(err2("editor-host", at, `the view "${editor}" doesn't open in ${host}`));
596
+ return;
597
+ }
598
+ const d = f;
599
+ if (!(d.type === "map" && isObject(d.of) && d.of.type === "record")) {
600
+ out.push({ severity: "warning", code: "editor-ignored", path: at, message: "only a map of records opens in an editor; this field is edited in place, so the editor is ignored" });
601
+ }
602
+ });
603
+ }
604
+ }
555
605
  function viewsFor(manifest, host) {
556
606
  const targeted = Object.keys(manifest.targets ?? {});
557
607
  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)));
@@ -599,6 +649,9 @@ function fieldListProblems(host, fields, path, topLevel, out) {
599
649
  if (field.editorOnly !== void 0 && !topLevel) {
600
650
  out.push(err2("editor-only-nested", atKey(at, "editorOnly"), "only a target's own fields can be editor-only, not a record's"));
601
651
  }
652
+ if (field.editor !== void 0 && !topLevel) {
653
+ out.push(err2("editor-nested", atKey(at, "editor"), "only a target's own fields open in a view, not a record's"));
654
+ }
602
655
  for (const flag of ["required", "editorOnly"]) {
603
656
  if (field[flag] !== void 0 && typeof field[flag] !== "boolean") {
604
657
  out.push(err2("not-a-boolean", atKey(at, flag), `${flag} is true or false`));
@@ -1085,6 +1138,6 @@ function orphanedEntries(data, exists) {
1085
1138
  return out;
1086
1139
  }
1087
1140
 
1088
- export { ATOMIC, COMMENTS, HOSTS, MANIFEST_FILE_SUFFIX, MANIFEST_SCHEMA, SCALAR_TYPES, SET, UNRESOLVED_KEY, VALUE_TYPES, atIndex, atKey, compileEntry, compiledTarget, defaultOf, entryProblems, extensionDataStrategy, isTextList, isViewEntry, keyedBy, manifestProblems, mapOf, markUnresolved, mergeComments, mergeExtensionData, mergeTree, objectOf, orphanedEntries, parseManifest, pickChoices, pickClashes, pickLists, renameStages, stableStringify, strategyForTarget, strategyForType, targetOf, unresolvedOf, unresolvedProblems, valueProblems, viewsFor, withoutMark };
1141
+ export { ATOMIC, COMMENTS, HOSTS, MANIFEST_FILE_SUFFIX, MANIFEST_SCHEMA, SCALAR_TYPES, SET, UNRESOLVED_KEY, VALUE_TYPES, atIndex, atKey, compileEntry, compiledTarget, defaultOf, entryProblems, extensionDataStrategy, isTextList, isViewEntry, keyedBy, manifestProblems, mapOf, markUnresolved, mergeComments, mergeExtensionData, mergeTree, missingRequirements, objectOf, orphanedEntries, parseManifest, pickChoices, pickClashes, pickLists, renameStages, stableStringify, strategyForTarget, strategyForType, targetOf, unresolvedOf, unresolvedProblems, valueProblems, viewsFor, withoutMark };
1089
1142
  //# sourceMappingURL=extensions.js.map
1090
1143
  //# sourceMappingURL=extensions.js.map