@wildwinter/toolkit 0.2.5 → 0.2.6

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.
@@ -111,6 +111,23 @@ interface Manifest {
111
111
  readonly docs?: string;
112
112
  readonly settings?: readonly SettingDecl[];
113
113
  readonly targets: Readonly<Partial<Record<HostName, Readonly<Record<string, TargetDecl>>>>>;
114
+ /** Views the extension's installed package supplies (tier 2), each a web component a host opens in a window. */
115
+ readonly views?: readonly ViewDecl[];
116
+ /** Other extensions whose data this one's views read, by id. */
117
+ readonly reads?: readonly string[];
118
+ }
119
+ /** A view: a web component in the extension's installed package, opened by a host in a floating window. */
120
+ interface ViewDecl {
121
+ /** Lower case, digits and hyphens, unique within the manifest. */
122
+ readonly id: string;
123
+ /** The window's title and the menu item. */
124
+ readonly name: string;
125
+ /** The component's script, a relative path within the package, such as `view/sheet.js`. */
126
+ readonly entry: string;
127
+ /** The custom element the script defines, such as `pattershot-sheet`. */
128
+ readonly element: string;
129
+ /** The hosts that offer it; every host the manifest targets when absent. */
130
+ readonly hosts?: readonly HostName[];
114
131
  }
115
132
  /** One thing wrong, with where it is. A host words it in its own report shape. */
116
133
  interface Problem {
@@ -145,6 +162,11 @@ interface UnresolvedEntry {
145
162
  declare function parseManifest(text: string): Manifest;
146
163
  /** Every problem with a manifest, by path. An empty list means it is sound. */
147
164
  declare function manifestProblems(manifest: unknown): Problem[];
165
+ /** A package path a view's entry may name: relative, forward slashes, no `.` or `..` segment, a `.js` file. */
166
+ declare function isViewEntry(entry: unknown): entry is string;
167
+ /** The views a host offers from a manifest: those naming the host, or every view when it names none and the
168
+ * manifest targets the host. A view that fails its checks is left out. */
169
+ declare function viewsFor(manifest: Manifest, host: HostName): ViewDecl[];
148
170
  /** The host field each of a manifest's settings names, the project's choice
149
171
  * first, else the setting's default. */
150
172
  declare function settingValues(manifest: Manifest, chosen?: Readonly<Record<string, unknown>>): Record<string, string>;
@@ -396,4 +418,4 @@ declare const atKey: (base: string, key: string) => string;
396
418
  /** `base` and then list position `i`. */
397
419
  declare const atIndex: (base: string, i: number) => string;
398
420
 
399
- export { ATOMIC, COMMENTS, type ClaimDecl, 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, type ResolvedClaim, SCALAR_TYPES, SET, type ScalarType, type SettingDecl, type StageRename, type Strategy, type TargetDecl, type TreeMergeOptions, type TreeMergeResult, type TypeDecl, UNRESOLVED_KEY, type UnresolvedEntry, VALUE_TYPES, type ValueContext, type ValueType, atIndex, atKey, claimClashes, compileEntry, compiledTarget, defaultOf, effectiveClaim, entryProblems, extensionDataStrategy, isTextList, keyedBy, manifestProblems, mapOf, markUnresolved, mergeComments, mergeExtensionData, mergeTree, objectOf, orphanedEntries, parseManifest, pickChoices, pickClashes, pickLists, renameStages, resolvedClaims, settingValues, stableStringify, strategyForTarget, strategyForType, targetOf, unresolvedOf, unresolvedProblems, valueProblems, withoutMark };
421
+ export { ATOMIC, COMMENTS, type ClaimDecl, 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, type ResolvedClaim, SCALAR_TYPES, SET, type ScalarType, type SettingDecl, 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, claimClashes, compileEntry, compiledTarget, defaultOf, effectiveClaim, entryProblems, extensionDataStrategy, isTextList, isViewEntry, keyedBy, manifestProblems, mapOf, markUnresolved, mergeComments, mergeExtensionData, mergeTree, objectOf, orphanedEntries, parseManifest, pickChoices, pickClashes, pickLists, renameStages, resolvedClaims, settingValues, stableStringify, strategyForTarget, strategyForType, targetOf, unresolvedOf, unresolvedProblems, valueProblems, viewsFor, withoutMark };
@@ -111,6 +111,23 @@ interface Manifest {
111
111
  readonly docs?: string;
112
112
  readonly settings?: readonly SettingDecl[];
113
113
  readonly targets: Readonly<Partial<Record<HostName, Readonly<Record<string, TargetDecl>>>>>;
114
+ /** Views the extension's installed package supplies (tier 2), each a web component a host opens in a window. */
115
+ readonly views?: readonly ViewDecl[];
116
+ /** Other extensions whose data this one's views read, by id. */
117
+ readonly reads?: readonly string[];
118
+ }
119
+ /** A view: a web component in the extension's installed package, opened by a host in a floating window. */
120
+ interface ViewDecl {
121
+ /** Lower case, digits and hyphens, unique within the manifest. */
122
+ readonly id: string;
123
+ /** The window's title and the menu item. */
124
+ readonly name: string;
125
+ /** The component's script, a relative path within the package, such as `view/sheet.js`. */
126
+ readonly entry: string;
127
+ /** The custom element the script defines, such as `pattershot-sheet`. */
128
+ readonly element: string;
129
+ /** The hosts that offer it; every host the manifest targets when absent. */
130
+ readonly hosts?: readonly HostName[];
114
131
  }
115
132
  /** One thing wrong, with where it is. A host words it in its own report shape. */
116
133
  interface Problem {
@@ -145,6 +162,11 @@ interface UnresolvedEntry {
145
162
  declare function parseManifest(text: string): Manifest;
146
163
  /** Every problem with a manifest, by path. An empty list means it is sound. */
147
164
  declare function manifestProblems(manifest: unknown): Problem[];
165
+ /** A package path a view's entry may name: relative, forward slashes, no `.` or `..` segment, a `.js` file. */
166
+ declare function isViewEntry(entry: unknown): entry is string;
167
+ /** The views a host offers from a manifest: those naming the host, or every view when it names none and the
168
+ * manifest targets the host. A view that fails its checks is left out. */
169
+ declare function viewsFor(manifest: Manifest, host: HostName): ViewDecl[];
148
170
  /** The host field each of a manifest's settings names, the project's choice
149
171
  * first, else the setting's default. */
150
172
  declare function settingValues(manifest: Manifest, chosen?: Readonly<Record<string, unknown>>): Record<string, string>;
@@ -396,4 +418,4 @@ declare const atKey: (base: string, key: string) => string;
396
418
  /** `base` and then list position `i`. */
397
419
  declare const atIndex: (base: string, i: number) => string;
398
420
 
399
- export { ATOMIC, COMMENTS, type ClaimDecl, 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, type ResolvedClaim, SCALAR_TYPES, SET, type ScalarType, type SettingDecl, type StageRename, type Strategy, type TargetDecl, type TreeMergeOptions, type TreeMergeResult, type TypeDecl, UNRESOLVED_KEY, type UnresolvedEntry, VALUE_TYPES, type ValueContext, type ValueType, atIndex, atKey, claimClashes, compileEntry, compiledTarget, defaultOf, effectiveClaim, entryProblems, extensionDataStrategy, isTextList, keyedBy, manifestProblems, mapOf, markUnresolved, mergeComments, mergeExtensionData, mergeTree, objectOf, orphanedEntries, parseManifest, pickChoices, pickClashes, pickLists, renameStages, resolvedClaims, settingValues, stableStringify, strategyForTarget, strategyForType, targetOf, unresolvedOf, unresolvedProblems, valueProblems, withoutMark };
421
+ export { ATOMIC, COMMENTS, type ClaimDecl, 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, type ResolvedClaim, SCALAR_TYPES, SET, type ScalarType, type SettingDecl, 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, claimClashes, compileEntry, compiledTarget, defaultOf, effectiveClaim, entryProblems, extensionDataStrategy, isTextList, isViewEntry, keyedBy, manifestProblems, mapOf, markUnresolved, mergeComments, mergeExtensionData, mergeTree, objectOf, orphanedEntries, parseManifest, pickChoices, pickClashes, pickLists, renameStages, resolvedClaims, settingValues, stableStringify, strategyForTarget, strategyForType, targetOf, unresolvedOf, unresolvedProblems, valueProblems, viewsFor, withoutMark };
@@ -360,7 +360,9 @@ var TYPE_KEYS = ["type", "values", "stages", "renamedStages", "to", "from", "fie
360
360
  var FIELD_KEYS = /* @__PURE__ */ new Set([...TYPE_KEYS, "name", "label", "help", "required", "group", "editorOnly"]);
361
361
  var CLAIM_KEYS = /* @__PURE__ */ new Set([...TYPE_KEYS, "setting", "label", "help", "required"]);
362
362
  var BARE_KEYS = new Set(TYPE_KEYS);
363
- var MANIFEST_KEYS = /* @__PURE__ */ new Set(["schema", "id", "name", "version", "docs", "settings", "targets"]);
363
+ var MANIFEST_KEYS = /* @__PURE__ */ new Set(["schema", "id", "name", "version", "docs", "settings", "targets", "views", "reads"]);
364
+ var VIEW_KEYS = /* @__PURE__ */ new Set(["id", "name", "entry", "element", "hosts"]);
365
+ var ELEMENT = /^[a-z][a-z0-9._]*-[a-z0-9._-]*$/;
364
366
  var err2 = (code, path, message) => ({ severity: "error", code, path, message });
365
367
  function parseManifest(text) {
366
368
  return parseSource(text);
@@ -382,6 +384,8 @@ function manifestProblems(manifest) {
382
384
  }
383
385
  if (m["docs"] !== void 0 && typeof m["docs"] !== "string") out.push(err2("docs", "docs", "docs is a link, as text"));
384
386
  const settings = settingsProblems(m["settings"], out);
387
+ viewsProblems(m["views"], m["targets"], out);
388
+ readsProblems(m["reads"], m["id"], out);
385
389
  const targets = m["targets"];
386
390
  if (!isObject(targets) || Object.keys(targets).length === 0) {
387
391
  out.push(err2("no-targets", "targets", "the manifest targets no host"));
@@ -480,6 +484,72 @@ function pickListProblems(kinds, hostPath, out) {
480
484
  target["fields"].forEach((f, i) => walk(f, atIndex(atKey(atKey(hostPath, kind), "fields"), i)));
481
485
  }
482
486
  }
487
+ function isViewEntry(entry) {
488
+ if (typeof entry !== "string" || !entry.endsWith(".js") || entry.includes("\\")) return false;
489
+ const parts = entry.split("/");
490
+ return parts.every((p) => p !== "" && p !== "." && p !== "..");
491
+ }
492
+ function viewsProblems(views, targets, out) {
493
+ if (views === void 0) return;
494
+ if (!Array.isArray(views)) {
495
+ out.push(err2("not-a-list", "views", "views are a list"));
496
+ return;
497
+ }
498
+ const ids = /* @__PURE__ */ new Set();
499
+ const elements = /* @__PURE__ */ new Set();
500
+ const targeted = isObject(targets) ? Object.keys(targets) : [];
501
+ views.forEach((v, i) => {
502
+ const at = atIndex("views", i);
503
+ if (!isObject(v)) {
504
+ out.push(err2("not-an-object", at, "a view is an object"));
505
+ return;
506
+ }
507
+ unknownKeys(v, VIEW_KEYS, at, out);
508
+ const id = v["id"];
509
+ if (typeof id !== "string" || !ID.test(id)) out.push(err2("view-id", atKey(at, "id"), "a view's id is lower case letters, digits and hyphens, starting with a letter"));
510
+ else if (ids.has(id)) out.push(err2("view-duplicate", atKey(at, "id"), `two views are called "${id}"`));
511
+ else ids.add(id);
512
+ if (typeof v["name"] !== "string" || v["name"].trim() === "") out.push(err2("view-name", atKey(at, "name"), "a view has a name, shown as its window's title"));
513
+ if (!isViewEntry(v["entry"])) {
514
+ out.push(err2("view-entry", atKey(at, "entry"), "a view's entry is a .js file in the extension's package, as a relative path such as view/sheet.js"));
515
+ }
516
+ const element = v["element"];
517
+ if (typeof element !== "string" || !ELEMENT.test(element)) {
518
+ out.push(err2("view-element", atKey(at, "element"), "a view's element is a custom element name: lower case, starting with a letter, with a hyphen, such as probe-view"));
519
+ } else if (elements.has(element)) out.push(err2("view-duplicate", atKey(at, "element"), `two views define <${element}>`));
520
+ else elements.add(element);
521
+ const hosts = v["hosts"];
522
+ if (hosts === void 0) return;
523
+ if (!Array.isArray(hosts) || hosts.length === 0) {
524
+ out.push(err2("view-hosts", atKey(at, "hosts"), "a view's hosts are a list of at least one host"));
525
+ return;
526
+ }
527
+ hosts.forEach((h, j) => {
528
+ const hp = atIndex(atKey(at, "hosts"), j);
529
+ if (typeof h !== "string" || !own(HOSTS, h)) out.push(err2("unknown-host", hp, `${JSON.stringify(h)} is not a host; a view opens in "patter" or "storylets"`));
530
+ else if (!targeted.includes(h)) out.push({ severity: "warning", code: "view-host-untargeted", path: hp, message: `the view opens in ${h}, but the manifest targets nothing there` });
531
+ });
532
+ });
533
+ }
534
+ function readsProblems(reads, self, out) {
535
+ if (reads === void 0) return;
536
+ if (!Array.isArray(reads)) {
537
+ out.push(err2("not-a-list", "reads", "reads is a list of extension ids"));
538
+ return;
539
+ }
540
+ const seen = /* @__PURE__ */ new Set();
541
+ reads.forEach((r, i) => {
542
+ const at = atIndex("reads", i);
543
+ if (typeof r !== "string" || !ID.test(r)) out.push(err2("reads-id", at, "an extension read is named by its id: lower case letters, digits and hyphens"));
544
+ else if (r === self) out.push({ severity: "warning", code: "reads-self", path: at, message: "an extension always reads its own data; this entry is ignored" });
545
+ else if (seen.has(r)) out.push(err2("reads-duplicate", at, `"${r}" is listed twice`));
546
+ else seen.add(r);
547
+ });
548
+ }
549
+ function viewsFor(manifest, host) {
550
+ const targeted = Object.keys(manifest.targets ?? {});
551
+ 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)));
552
+ }
483
553
  function unknownKeys(obj, known, path, out) {
484
554
  for (const k of Object.keys(obj)) {
485
555
  if (!known.has(k)) {
@@ -1117,6 +1187,6 @@ function orphanedEntries(data, exists) {
1117
1187
  return out;
1118
1188
  }
1119
1189
 
1120
- export { ATOMIC, COMMENTS, HOSTS, MANIFEST_FILE_SUFFIX, MANIFEST_SCHEMA, SCALAR_TYPES, SET, UNRESOLVED_KEY, VALUE_TYPES, atIndex, atKey, claimClashes, compileEntry, compiledTarget, defaultOf, effectiveClaim, entryProblems, extensionDataStrategy, isTextList, keyedBy, manifestProblems, mapOf, markUnresolved, mergeComments, mergeExtensionData, mergeTree, objectOf, orphanedEntries, parseManifest, pickChoices, pickClashes, pickLists, renameStages, resolvedClaims, settingValues, stableStringify, strategyForTarget, strategyForType, targetOf, unresolvedOf, unresolvedProblems, valueProblems, withoutMark };
1190
+ export { ATOMIC, COMMENTS, HOSTS, MANIFEST_FILE_SUFFIX, MANIFEST_SCHEMA, SCALAR_TYPES, SET, UNRESOLVED_KEY, VALUE_TYPES, atIndex, atKey, claimClashes, compileEntry, compiledTarget, defaultOf, effectiveClaim, entryProblems, extensionDataStrategy, isTextList, isViewEntry, keyedBy, manifestProblems, mapOf, markUnresolved, mergeComments, mergeExtensionData, mergeTree, objectOf, orphanedEntries, parseManifest, pickChoices, pickClashes, pickLists, renameStages, resolvedClaims, settingValues, stableStringify, strategyForTarget, strategyForType, targetOf, unresolvedOf, unresolvedProblems, valueProblems, viewsFor, withoutMark };
1121
1191
  //# sourceMappingURL=extensions.js.map
1122
1192
  //# sourceMappingURL=extensions.js.map