@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.
- package/CHANGELOG.md +12 -0
- package/conformance/extensions/manifest.json +275 -0
- package/dist/extensions.cjs +56 -2
- package/dist/extensions.cjs.map +1 -1
- package/dist/extensions.d.cts +9 -1
- package/dist/extensions.d.ts +9 -1
- package/dist/extensions.js +56 -3
- package/dist/extensions.js.map +1 -1
- package/extensions.md +17 -0
- package/package.json +1 -1
package/dist/extensions.d.cts
CHANGED
|
@@ -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 };
|
package/dist/extensions.d.ts
CHANGED
|
@@ -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 };
|
package/dist/extensions.js
CHANGED
|
@@ -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
|