@markii/host 0.0.1 → 0.17.0-next.3

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.
Files changed (193) hide show
  1. package/README.md +20 -2
  2. package/dist/browser-worker.js +639 -0
  3. package/dist/browser.d.ts +61 -0
  4. package/dist/browser.js +172 -0
  5. package/dist/conformance/host/01-plain-open/expected.json +5 -0
  6. package/dist/conformance/host/01-plain-open/note.mk.md +8 -0
  7. package/dist/conformance/host/01-plain-open/scenario.json +6 -0
  8. package/dist/conformance/host/02-run-manual-grant-accepted/expected.json +16 -0
  9. package/dist/conformance/host/02-run-manual-grant-accepted/note.mk.md +5 -0
  10. package/dist/conformance/host/02-run-manual-grant-accepted/scenario.json +14 -0
  11. package/dist/conformance/host/03-run-manual-grant-refused/expected.json +18 -0
  12. package/dist/conformance/host/03-run-manual-grant-refused/note.mk.md +5 -0
  13. package/dist/conformance/host/03-run-manual-grant-refused/scenario.json +14 -0
  14. package/dist/conformance/host/04-run-auto-write-denied/expected.json +16 -0
  15. package/dist/conformance/host/04-run-auto-write-denied/note.mk.md +10 -0
  16. package/dist/conformance/host/04-run-auto-write-denied/scenario.json +13 -0
  17. package/dist/conformance/host/05-export-html-pack-directive/expected.json +14 -0
  18. package/dist/conformance/host/05-export-html-pack-directive/note.mk.md +8 -0
  19. package/dist/conformance/host/05-export-html-pack-directive/scenario.json +6 -0
  20. package/dist/conformance/host/06-install-pack-then-render/expected.json +18 -0
  21. package/dist/conformance/host/06-install-pack-then-render/files/ana-1.0.0/pack.json +13 -0
  22. package/dist/conformance/host/06-install-pack-then-render/files/ana-1.0.0/webview.js +1 -0
  23. package/dist/conformance/host/06-install-pack-then-render/note.mk.md +5 -0
  24. package/dist/conformance/host/06-install-pack-then-render/scenario.json +15 -0
  25. package/dist/conformance/host/07-complete-in-directive/expected.json +14 -0
  26. package/dist/conformance/host/07-complete-in-directive/note.mk.md +3 -0
  27. package/dist/conformance/host/07-complete-in-directive/scenario.json +6 -0
  28. package/dist/conformance/host/08-hover-standard-component/expected.json +5 -0
  29. package/dist/conformance/host/08-hover-standard-component/note.mk.md +5 -0
  30. package/dist/conformance/host/08-hover-standard-component/scenario.json +6 -0
  31. package/dist/conformance/host/09-insert-skeleton/expected.json +5 -0
  32. package/dist/conformance/host/09-insert-skeleton/note.mk.md +4 -0
  33. package/dist/conformance/host/09-insert-skeleton/scenario.json +6 -0
  34. package/dist/conformance/host/10-scripts-disabled/expected.json +11 -0
  35. package/dist/conformance/host/10-scripts-disabled/note.mk.md +5 -0
  36. package/dist/conformance/host/10-scripts-disabled/scenario.json +9 -0
  37. package/dist/conformance/host/11-complete-closing-fence/expected.json +5 -0
  38. package/dist/conformance/host/11-complete-closing-fence/note.mk.md +5 -0
  39. package/dist/conformance/host/11-complete-closing-fence/scenario.json +12 -0
  40. package/dist/conformance/host/12-complete-nested-container/expected.json +5 -0
  41. package/dist/conformance/host/12-complete-nested-container/note.mk.md +5 -0
  42. package/dist/conformance/host/12-complete-nested-container/scenario.json +12 -0
  43. package/dist/conformance/host/13-install-pack-declined/expected.json +12 -0
  44. package/dist/conformance/host/13-install-pack-declined/files/ana-1.0.0/pack.json +13 -0
  45. package/dist/conformance/host/13-install-pack-declined/files/ana-1.0.0/webview.js +1 -0
  46. package/dist/conformance/host/13-install-pack-declined/note.mk.md +5 -0
  47. package/dist/conformance/host/13-install-pack-declined/scenario.json +10 -0
  48. package/dist/conformance/host/14-remove-pack/expected.json +14 -0
  49. package/dist/conformance/host/14-remove-pack/files/ana-1.0.0/pack.json +13 -0
  50. package/dist/conformance/host/14-remove-pack/files/ana-1.0.0/webview.js +1 -0
  51. package/dist/conformance/host/14-remove-pack/note.mk.md +5 -0
  52. package/dist/conformance/host/14-remove-pack/scenario.json +11 -0
  53. package/dist/conformance/host/15-load-packs-engine-views/expected.json +19 -0
  54. package/dist/conformance/host/15-load-packs-engine-views/files/duo-1.0.0/pack.json +9 -0
  55. package/dist/conformance/host/15-load-packs-engine-views/files/duo-1.0.0/webview.js +1 -0
  56. package/dist/conformance/host/15-load-packs-engine-views/note.mk.md +9 -0
  57. package/dist/conformance/host/15-load-packs-engine-views/scenario.json +10 -0
  58. package/dist/conformance/host/16-pack-trust/expected.json +37 -0
  59. package/dist/conformance/host/16-pack-trust/files/ana-1.0.0/pack.json +12 -0
  60. package/dist/conformance/host/16-pack-trust/files/ana-1.0.0/webview.js +1 -0
  61. package/dist/conformance/host/16-pack-trust/files/sneaky-1.0.0/pack.json +12 -0
  62. package/dist/conformance/host/16-pack-trust/files/sneaky-1.0.0/webview.js +1 -0
  63. package/dist/conformance/host/16-pack-trust/note.mk.md +5 -0
  64. package/dist/conformance/host/16-pack-trust/scenario.json +26 -0
  65. package/dist/conformance/host/README.md +221 -0
  66. package/dist/conformance.d.ts +2 -0
  67. package/dist/conformance.js +5 -0
  68. package/dist/diagnostics/render-diagnostics.d.ts +41 -0
  69. package/dist/diagnostics/render-diagnostics.js +46 -0
  70. package/dist/export/cascade.d.ts +98 -0
  71. package/dist/export/cascade.js +159 -0
  72. package/dist/export/export-zip.d.ts +19 -0
  73. package/dist/export/export-zip.js +38 -0
  74. package/dist/export/image-embed.d.ts +126 -0
  75. package/dist/export/image-embed.js +274 -0
  76. package/dist/export/note-export.d.ts +332 -0
  77. package/dist/export/note-export.js +366 -0
  78. package/dist/export/note-links.d.ts +83 -0
  79. package/dist/export/note-links.js +241 -0
  80. package/dist/glue.wasm +0 -0
  81. package/dist/host/adapter.d.ts +218 -0
  82. package/dist/host/adapter.js +9 -0
  83. package/dist/host/cascade-export-behavior.d.ts +120 -0
  84. package/dist/host/cascade-export-behavior.js +151 -0
  85. package/dist/host/conformance-runner.d.ts +159 -0
  86. package/dist/host/conformance-runner.js +632 -0
  87. package/dist/host/create-host.d.ts +251 -0
  88. package/dist/host/create-host.js +329 -0
  89. package/dist/host/editor-behavior.d.ts +153 -0
  90. package/dist/host/editor-behavior.js +223 -0
  91. package/dist/host/export/md-plain.d.ts +15 -0
  92. package/dist/host/export/md-plain.js +125 -0
  93. package/dist/host/export-behavior.d.ts +111 -0
  94. package/dist/host/export-behavior.js +257 -0
  95. package/dist/host/labels.d.ts +23 -0
  96. package/dist/host/labels.js +32 -0
  97. package/dist/host/pack-archive.d.ts +30 -0
  98. package/dist/host/pack-archive.js +62 -0
  99. package/dist/host/pack-install.d.ts +87 -0
  100. package/dist/host/pack-install.js +186 -0
  101. package/dist/host/pack-load.d.ts +71 -0
  102. package/dist/host/pack-load.js +57 -0
  103. package/dist/host/pack-remove.d.ts +21 -0
  104. package/dist/host/pack-remove.js +65 -0
  105. package/dist/host/pack-trust-behavior.d.ts +79 -0
  106. package/dist/host/pack-trust-behavior.js +262 -0
  107. package/dist/host/refresh-interval.d.ts +37 -0
  108. package/dist/host/refresh-interval.js +59 -0
  109. package/dist/host/run-behavior.d.ts +88 -0
  110. package/dist/host/run-behavior.js +189 -0
  111. package/dist/host/run-diagnostics.d.ts +26 -0
  112. package/dist/host/run-diagnostics.js +43 -0
  113. package/dist/host/script-execution.d.ts +72 -0
  114. package/dist/host/script-execution.js +68 -0
  115. package/dist/host/terminal-packs.d.ts +39 -0
  116. package/dist/host/terminal-packs.js +51 -0
  117. package/dist/html-engine.d.ts +2 -0
  118. package/dist/html-engine.js +29 -0
  119. package/dist/index.d.ts +95 -0
  120. package/dist/index.js +69 -0
  121. package/dist/insert/component-catalog.d.ts +23 -0
  122. package/dist/insert/component-catalog.js +96 -0
  123. package/dist/node-fs.d.ts +14 -0
  124. package/dist/node-fs.js +70 -0
  125. package/dist/node-worker.js +20657 -0
  126. package/dist/node.d.ts +6 -0
  127. package/dist/node.js +13 -0
  128. package/dist/package.json +3 -0
  129. package/dist/packs/discover.d.ts +119 -0
  130. package/dist/packs/discover.js +266 -0
  131. package/dist/packs/pack-diagnostics.d.ts +79 -0
  132. package/dist/packs/pack-diagnostics.js +103 -0
  133. package/dist/packs/pack-paths.d.ts +53 -0
  134. package/dist/packs/pack-paths.js +105 -0
  135. package/dist/packs/pack-render-registry.d.ts +82 -0
  136. package/dist/packs/pack-render-registry.js +182 -0
  137. package/dist/packs/pack-scripts.d.ts +20 -0
  138. package/dist/packs/pack-scripts.js +96 -0
  139. package/dist/packs/pack-trust.d.ts +57 -0
  140. package/dist/packs/pack-trust.js +111 -0
  141. package/dist/packs/prebuilt.d.ts +49 -0
  142. package/dist/packs/prebuilt.js +97 -0
  143. package/dist/path-util.d.ts +24 -0
  144. package/dist/path-util.js +82 -0
  145. package/dist/run/browser-isolate.d.ts +45 -0
  146. package/dist/run/browser-isolate.js +85 -0
  147. package/dist/run/bundle-document.d.ts +29 -0
  148. package/dist/run/bundle-document.js +40 -0
  149. package/dist/run/bundle-run.d.ts +188 -0
  150. package/dist/run/bundle-run.js +232 -0
  151. package/dist/run/grant-flow.d.ts +232 -0
  152. package/dist/run/grant-flow.js +390 -0
  153. package/dist/run/ip-address.d.ts +41 -0
  154. package/dist/run/ip-address.js +255 -0
  155. package/dist/run/isolate.d.ts +59 -0
  156. package/dist/run/isolate.js +22 -0
  157. package/dist/run/lua-resolver.d.ts +21 -0
  158. package/dist/run/lua-resolver.js +63 -0
  159. package/dist/run/net-bridge-worker.d.ts +26 -0
  160. package/dist/run/net-bridge-worker.js +66 -0
  161. package/dist/run/net-bridge.d.ts +56 -0
  162. package/dist/run/net-bridge.js +59 -0
  163. package/dist/run/net-pinning.d.ts +82 -0
  164. package/dist/run/net-pinning.js +137 -0
  165. package/dist/run/net-provider.d.ts +72 -0
  166. package/dist/run/net-provider.js +303 -0
  167. package/dist/run/node-isolate.d.ts +22 -0
  168. package/dist/run/node-isolate.js +68 -0
  169. package/dist/run/run-flow.d.ts +186 -0
  170. package/dist/run/run-flow.js +323 -0
  171. package/dist/run/run-host.d.ts +107 -0
  172. package/dist/run/run-host.js +241 -0
  173. package/dist/run/run-job.d.ts +130 -0
  174. package/dist/run/run-job.js +279 -0
  175. package/dist/run/run-progress.d.ts +55 -0
  176. package/dist/run/run-progress.js +42 -0
  177. package/dist/run/run-trace.d.ts +44 -0
  178. package/dist/run/run-trace.js +36 -0
  179. package/dist/run/script-requirements.d.ts +98 -0
  180. package/dist/run/script-requirements.js +212 -0
  181. package/dist/run/snapshot-storage.d.ts +25 -0
  182. package/dist/run/snapshot-storage.js +31 -0
  183. package/dist/run/stale-values.d.ts +25 -0
  184. package/dist/run/stale-values.js +32 -0
  185. package/dist/run/worker-entry-browser.d.ts +62 -0
  186. package/dist/run/worker-entry-browser.js +172 -0
  187. package/dist/run/worker-entry.d.ts +1 -0
  188. package/dist/run/worker-entry.js +98 -0
  189. package/dist/values-failure.d.ts +17 -0
  190. package/dist/values-failure.js +1 -0
  191. package/dist/values-merge.d.ts +39 -0
  192. package/dist/values-merge.js +33 -0
  193. package/package.json +80 -4
package/dist/node.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export type { SpawnRunOptions } from './run/run-host.js';
2
+ export type { NodeHostIsolateOptions } from './run/node-isolate.js';
3
+ export { createNodeHostIsolate, workerThreadIsolate, } from './run/node-isolate.js';
4
+ export { createNetProvider } from './run/net-provider.js';
5
+ export { createNodeArchiveExtractFs, createNodeDirectoryLister, createNodeFileReader, createNodePackDiscoveryIo, createNodeReader as createNodePackScriptsReader, } from './node-fs.js';
6
+ export { createHtmlExportEngine } from './html-engine.js';
package/dist/node.js ADDED
@@ -0,0 +1,13 @@
1
+ // @markii/host/node: the half of this package that needs Node, or that
2
+ // carries a heavy engine dependency the main entry must not.
3
+ //
4
+ // The main entry (`./index.ts`) is environment-neutral. This entry adds:
5
+ // - the worker_threads isolate and the pinned network provider (`node:*`);
6
+ // - the Node-backed file-system seams the neutral code takes by injection;
7
+ // - the `@markii/html`-backed static export engine;
8
+ //
9
+ // A Node host imports from both entries. No browser bundle imports this one.
10
+ export { createNodeHostIsolate, workerThreadIsolate, } from './run/node-isolate.js';
11
+ export { createNetProvider } from './run/net-provider.js';
12
+ export { createNodeArchiveExtractFs, createNodeDirectoryLister, createNodeFileReader, createNodePackDiscoveryIo, createNodeReader as createNodePackScriptsReader, } from './node-fs.js';
13
+ export { createHtmlExportEngine } from './html-engine.js';
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,119 @@
1
+ import type { PackManifest } from '@markii/pack';
2
+ import type { UnavailableComponent } from '@markii/stdlib';
3
+ /**
4
+ * What a valid pack that declares nothing for this host's engine carries on
5
+ * its skipped entry: its namespace, and every component it declares with
6
+ * the engines each one does ship a view for, so a note that uses one can say
7
+ * "no view here" rather than "unknown component".
8
+ */
9
+ export interface EngineUnsupportedPack {
10
+ readonly packName: string;
11
+ readonly components: readonly UnavailableComponent[];
12
+ }
13
+ /**
14
+ * Every component a pack declares, as `{ directive, pack, engines }` with
15
+ * the engine ids sorted. The shape a host hands a renderer's
16
+ * `unavailableComponents` option after filtering.
17
+ */
18
+ export declare function declaredPackComponents(manifest: Pick<PackManifest, 'name' | 'components' | 'engine'>): UnavailableComponent[];
19
+ /** The components, from `declared`, that ship no view for `engine`. */
20
+ export declare function componentsWithoutEngineView(declared: readonly UnavailableComponent[], engine: string): UnavailableComponent[];
21
+ /** Reads a file's UTF-8 text, or resolves `undefined` if it does not exist / cannot be read. Never rejects for an ordinary "not found" — injected so this module needs no real filesystem to test. */
22
+ export type PackFileReader = (absolutePath: string) => Promise<string | undefined>;
23
+ /** Lists one directory's immediate entries, or `[]` if it does not exist / cannot be read / is not a directory. Never rejects — injected (like `PackFileReader`) so the one-level parent-folder scan below is testable without real disk. */
24
+ export type PackDirectoryLister = (absoluteDir: string) => Promise<ReadonlyArray<{
25
+ name: string;
26
+ isDirectory: boolean;
27
+ }>>;
28
+ /** One successfully discovered, validated pack. */
29
+ export interface DiscoveredPack {
30
+ /** The configured folder this pack was discovered in (absolute path, as given). */
31
+ readonly folder: string;
32
+ readonly manifest: PackManifest;
33
+ /** Local component name -> absolute path to its declared source file (`manifest.components[name]`, resolved against `folder`). Informational: what a host consumes them for (bundling, `import()`, or locating the pack's prebuilt registration script) is that host's own concern. */
34
+ readonly componentPaths: Readonly<Record<string, string>>;
35
+ /** Engine id -> (local component name -> absolute source path), for every engine any component declares a source for. `componentPaths` is the `react` slice of this. A terminal build (`ansi`) reads its slice here. */
36
+ readonly componentPathsByEngine?: Readonly<Record<string, Readonly<Record<string, string>>>>;
37
+ /** Absolute path to this pack's `scripts/` directory (docs/packs.md: shared Lua modules travel here). The directory need not actually exist — a pack with no shared Lua simply has nothing under it. */
38
+ readonly scriptsDir: string;
39
+ /**
40
+ * Absolute path to this pack's prebuilt registration script (`webview.js`,
41
+ * sibling to `pack.json`) — a HOST-SPECIFIC CONVENTION layered on top of
42
+ * the neutral pack contract (`@markii/pack`'s `PackManifest.components`
43
+ * only names pack-relative SOURCE paths for a bundler, per
44
+ * docs/packs.md; it says nothing about a prebuilt browser artifact).
45
+ * Named `scriptPath`, not `webviewScriptPath`: this field is read by
46
+ * every host that loads a pack's compiled registration script, not only
47
+ * a host with an actual webview (e.g. an Obsidian plugin evaluates it
48
+ * in-process — see that host's `pack-runtime.ts`), and this package has
49
+ * no webview of its own. The on-disk filename convention (`webview.js`)
50
+ * is unchanged — a script authored for one host still works for another,
51
+ * since the compiled-artifact contract itself
52
+ * (`window.__markiiRegisterPack`/lazy `window.__markiiReact`) is
53
+ * host-neutral (see `./pack-build.ts`'s top doc comment). The file need
54
+ * not actually exist — a pack with no such script simply never
55
+ * contributes anything to a host's registry, and every directive under
56
+ * its namespace falls through to the standard unknown-component
57
+ * fallback (the same engine-gating posture `@markii/react`'s `loadPack`
58
+ * already takes for an unsupported `engine`).
59
+ */
60
+ readonly scriptPath: string;
61
+ /**
62
+ * Absolute path to this pack's emitted stylesheet, when one exists — a
63
+ * SIBLING of `scriptPath`'s compiled output (`./pack-build.ts`, same
64
+ * cache directory, same content-hash base name, `.css` in place of
65
+ * `.js`). `undefined` here always: discovery itself never scans for one,
66
+ * only a host's own pack-context composition knows whether a build
67
+ * actually produced a stylesheet — it overrides this field on the
68
+ * `DiscoveredPack` copy it uses once `buildPackRegistrationScript`'s
69
+ * outcome carries a `stylesheetPath`. A pack that ships its own prebuilt
70
+ * `webview.js` (no compile step at all) has no established
71
+ * sibling-stylesheet convention and never gets one here either.
72
+ */
73
+ readonly stylesheetPath?: string;
74
+ }
75
+ /** One folder that did not produce a usable pack, and why — developer-facing only, never shown as page content. */
76
+ export interface SkippedPackFolder {
77
+ readonly folder: string;
78
+ readonly reason: string;
79
+ /** Set when the pack is valid but declares nothing for this host's renderer engine (an expected state, not a failure). `packName` is its namespace. */
80
+ readonly engineUnsupported?: EngineUnsupportedPack;
81
+ }
82
+ export interface DiscoverPacksResult {
83
+ /** Every pack that validated AND whose namespace does not collide with another discovered pack. Empty when every configured folder was invalid or every valid pack collided. */
84
+ readonly packs: readonly DiscoveredPack[];
85
+ /** Namespaces that appeared more than once across the configured folders (docs/packs.md: "Installing two packs with the same namespace is rejected at install time") — every pack sharing a colliding namespace is excluded from `packs`, not just the second one, so install stays all-or-nothing per namespace. */
86
+ readonly collisions: readonly string[];
87
+ /** Folders that produced no pack, with a short reason each (missing/unreadable `pack.json`, malformed JSON, failed manifest validation). */
88
+ readonly skipped: readonly SkippedPackFolder[];
89
+ }
90
+ /**
91
+ * Discovers and validates a pack in each of `folders`, in order, then
92
+ * removes (and reports via `collisions`) every pack whose namespace repeats
93
+ * across the set. Duplicate folder entries in `folders` collapse to one
94
+ * discovery attempt each (`Set`), so a repeated setting entry can never
95
+ * itself manufacture a spurious collision.
96
+ *
97
+ * `supportedEngine`, when given, additionally skips (rather than
98
+ * discovers) any pack whose manifest names a different `engine` — see
99
+ * `checkEngine`. Omitted (the default), a pack's engine is not checked
100
+ * here at all, matching this function's behavior before this parameter
101
+ * existed; a caller that only lists packs for a purpose engine-agnostic
102
+ * (e.g. `@markii/pack`'s own tooling) is unaffected.
103
+ *
104
+ * ONE-LEVEL PARENT-FOLDER SCAN: when a configured folder has no `pack.json`
105
+ * of its own (not "has one that fails validation" — see `ManifestAttempt`'s
106
+ * doc comment), its immediate subfolders are probed the same way, and each
107
+ * one that DOES have a valid `pack.json` counts as its own discovered pack
108
+ * (`folder` set to the child path). This lets a user configure one parent
109
+ * (`packs`) that holds several pack folders (`packs/pack1`, `packs/pack2`)
110
+ * instead of listing each one in the host's setting. Exactly one level
111
+ * deep, never recursive: a grandchild's `pack.json` is never found this
112
+ * way. A parent with neither its own manifest nor any child manifest is
113
+ * skipped exactly as before ("no readable pack.json"), and a child that
114
+ * DOES have a `pack.json` but fails validation is skipped individually, by
115
+ * its own (child) folder path.
116
+ */
117
+ export declare function discoverPacks(folders: readonly string[], readFile: PackFileReader, listDirectory: PackDirectoryLister, supportedEngine?: string): Promise<DiscoverPacksResult>;
118
+ /** The discovered packs' namespaces — what `resolveUses` (`@markii/pack`) checks a note's `uses:` declaration against. */
119
+ export declare function installedNamespaces(packs: readonly DiscoveredPack[]): string[];
@@ -0,0 +1,266 @@
1
+ /**
2
+ * Node-side, host-UI-free discovery of installed component packs
3
+ * (docs/packs.md, GitHub issue #3 slice 5). Given the folders named by a
4
+ * host's own pack-folder setting (VS Code's `markii.packs`, this plugin's
5
+ * device-local pack-folder list, or any future host's equivalent), reads
6
+ * each folder's `pack.json`, validates it with `@markii/pack`'s
7
+ * `parsePackManifest`, and returns a model of what was found: the manifest
8
+ * plus absolute, host-resolved paths to the pack-relative locations a
9
+ * later step needs (each component's declared source, and the pack's
10
+ * `scripts/` directory for shared Lua modules).
11
+ *
12
+ * Lives in `@markii/host` (not any one app) because it is pure logic over
13
+ * paths, manifests, and strings — no `vscode`, no `obsidian`, no React, and
14
+ * no knowledge of which host is calling it. `apps/vscode/src/packs/`'s
15
+ * `apps/obsidian/src/packs/` copies of this file (pre-dating this hoist)
16
+ * differed only in the field names below (`scriptPath`/`stylesheetPath` —
17
+ * see those fields' own doc comments for why the neutral names won).
18
+ *
19
+ * Filesystem access is injected (`readFile`) rather than imported directly,
20
+ * so this whole module is testable with plain in-memory fakes.
21
+ *
22
+ * Cleanliness (AGENTS.md): a folder with no `pack.json`, malformed JSON, or
23
+ * a manifest that fails validation is QUIETLY skipped — never a thrown
24
+ * error or a dump in the UI. A caller that wants to surface *why* a pack
25
+ * failed to load can read `DiscoverPacksResult.skipped`, which is optional,
26
+ * developer-facing detail (a host's own diagnostics surface), never
27
+ * something shown as page content.
28
+ */
29
+ import { joinPath } from '../path-util.js';
30
+ import { parsePackManifest } from '@markii/pack';
31
+ import { composeDirectiveName, detectNamespaceCollisions, packComponents, packComponentsForEngine, packDeclaredEngines, packSupportsEngine, } from '@markii/pack';
32
+ /**
33
+ * Every component a pack declares, as `{ directive, pack, engines }` with
34
+ * the engine ids sorted. The shape a host hands a renderer's
35
+ * `unavailableComponents` option after filtering.
36
+ */
37
+ export function declaredPackComponents(manifest) {
38
+ const declared = [];
39
+ for (const listing of packComponents(manifest)) {
40
+ const composed = composeDirectiveName(manifest.name, listing.localName);
41
+ if (!composed.ok)
42
+ continue;
43
+ declared.push({
44
+ directive: composed.name,
45
+ pack: manifest.name,
46
+ engines: Object.keys(listing.sources).sort(),
47
+ });
48
+ }
49
+ return declared;
50
+ }
51
+ /** The components, from `declared`, that ship no view for `engine`. */
52
+ export function componentsWithoutEngineView(declared, engine) {
53
+ return declared.filter((component) => !component.engines.includes(engine));
54
+ }
55
+ /** A sane upper bound on how many immediate children of one configured folder the one-level parent-folder scan (`discoverPacks`) will probe for a `pack.json` — defense in depth against a pathological folder (thousands of entries, a symlink farm) turning "load the installed packs" into an unbounded scan. Matches the spirit of `./pack-scripts.ts`'s `MAX_SCRIPT_FILES_PER_PACK`. */
56
+ const MAX_CHILD_FOLDERS_PER_PARENT = 200;
57
+ /**
58
+ * The engine whose source paths fill `DiscoveredPack.componentPaths`. A
59
+ * data-field name from the pack contract, not a renderer dependency.
60
+ */
61
+ const COMPONENT_PATHS_ENGINE = 'react';
62
+ /** `folder`/`pack.json`, joined with the host path separator. */
63
+ function manifestPathFor(folder) {
64
+ return joinPath(folder, 'pack.json');
65
+ }
66
+ async function tryLoadPackAt(folder, readFile) {
67
+ const manifestPath = manifestPathFor(folder);
68
+ let text;
69
+ try {
70
+ text = await readFile(manifestPath);
71
+ }
72
+ catch {
73
+ text = undefined;
74
+ }
75
+ if (text === undefined) {
76
+ return { kind: 'missing' };
77
+ }
78
+ const result = parsePackManifest(text);
79
+ if (!result.ok) {
80
+ return {
81
+ kind: 'invalid',
82
+ reason: `invalid pack.json (${result.errors.join('; ')})`,
83
+ };
84
+ }
85
+ // Read the manifest's components map only through @markii/pack's
86
+ // packComponents() — the one accessor that normalizes both the string
87
+ // shorthand and the object form (source/description/kind), so this
88
+ // module never re-implements that walk or its Object.hasOwn guarding.
89
+ const componentPaths = {};
90
+ for (const listing of packComponentsForEngine(result.manifest, COMPONENT_PATHS_ENGINE)) {
91
+ componentPaths[listing.localName] = joinPath(folder, listing.source);
92
+ }
93
+ const componentPathsByEngine = {};
94
+ for (const listing of packComponents(result.manifest)) {
95
+ for (const [engine, source] of Object.entries(listing.sources)) {
96
+ const slice = (componentPathsByEngine[engine] ??= {});
97
+ slice[listing.localName] = joinPath(folder, source);
98
+ }
99
+ }
100
+ return {
101
+ kind: 'found',
102
+ pack: {
103
+ folder,
104
+ manifest: result.manifest,
105
+ componentPaths,
106
+ componentPathsByEngine,
107
+ scriptsDir: joinPath(folder, 'scripts'),
108
+ scriptPath: joinPath(folder, 'webview.js'),
109
+ },
110
+ };
111
+ }
112
+ /**
113
+ * Discovers and validates a pack in each of `folders`, in order, then
114
+ * removes (and reports via `collisions`) every pack whose namespace repeats
115
+ * across the set. Duplicate folder entries in `folders` collapse to one
116
+ * discovery attempt each (`Set`), so a repeated setting entry can never
117
+ * itself manufacture a spurious collision.
118
+ *
119
+ * ONE-LEVEL PARENT-FOLDER SCAN: when a configured folder has no `pack.json`
120
+ * of its own (not "has one that fails validation" — see `ManifestAttempt`'s
121
+ * doc comment), its immediate subfolders are probed the same way, and each
122
+ * one that DOES have a valid `pack.json` counts as its own discovered pack
123
+ * (`folder` set to the child path). This lets a user configure one parent
124
+ * (`packs`) that holds several pack folders (`packs/pack1`, `packs/pack2`)
125
+ * instead of listing each one in the host's setting. Exactly one level
126
+ * deep, never recursive: a grandchild's `pack.json` is never found this
127
+ * way. A parent with neither its own manifest nor any child manifest is
128
+ * skipped exactly as before ("no readable pack.json"), and a child that
129
+ * DOES have a `pack.json` but fails validation is skipped individually, by
130
+ * its own (child) folder path.
131
+ */
132
+ /**
133
+ * When a `ManifestAttempt` succeeded (`kind: 'found'`) but the manifest's
134
+ * `engine` does not match `supportedEngine`, downgrades it to
135
+ * a `SkippedPackFolder` instead of a `DiscoveredPack` — same effect
136
+ * `@markii/react`'s `loadPack` already has for an unsupported engine
137
+ * (nothing registered for that namespace, unknown-component fallback for
138
+ * every directive under it), but recorded here too so it also drops out of
139
+ * a host's Insert Component / completion catalog (`discoverConfiguredPacks`
140
+ * consumers) instead of offering a snippet for a component nothing can
141
+ * ever render, and so the skip reaches the SAME diagnostics line every
142
+ * other skip does — never silent.
143
+ */
144
+ function checkEngine(pack, supportedEngine) {
145
+ if (supportedEngine === undefined ||
146
+ packSupportsEngine(pack.manifest, supportedEngine)) {
147
+ return { kind: 'found', pack };
148
+ }
149
+ return {
150
+ kind: 'invalid',
151
+ reason: `pack engine "${packDeclaredEngines(pack.manifest)}" is not supported here (expected "${supportedEngine}"); its components fall back to the unknown-component box`,
152
+ engineUnsupported: {
153
+ packName: pack.manifest.name,
154
+ components: declaredPackComponents(pack.manifest),
155
+ },
156
+ };
157
+ }
158
+ /**
159
+ * Discovers and validates a pack in each of `folders`, in order, then
160
+ * removes (and reports via `collisions`) every pack whose namespace repeats
161
+ * across the set. Duplicate folder entries in `folders` collapse to one
162
+ * discovery attempt each (`Set`), so a repeated setting entry can never
163
+ * itself manufacture a spurious collision.
164
+ *
165
+ * `supportedEngine`, when given, additionally skips (rather than
166
+ * discovers) any pack whose manifest names a different `engine` — see
167
+ * `checkEngine`. Omitted (the default), a pack's engine is not checked
168
+ * here at all, matching this function's behavior before this parameter
169
+ * existed; a caller that only lists packs for a purpose engine-agnostic
170
+ * (e.g. `@markii/pack`'s own tooling) is unaffected.
171
+ *
172
+ * ONE-LEVEL PARENT-FOLDER SCAN: when a configured folder has no `pack.json`
173
+ * of its own (not "has one that fails validation" — see `ManifestAttempt`'s
174
+ * doc comment), its immediate subfolders are probed the same way, and each
175
+ * one that DOES have a valid `pack.json` counts as its own discovered pack
176
+ * (`folder` set to the child path). This lets a user configure one parent
177
+ * (`packs`) that holds several pack folders (`packs/pack1`, `packs/pack2`)
178
+ * instead of listing each one in the host's setting. Exactly one level
179
+ * deep, never recursive: a grandchild's `pack.json` is never found this
180
+ * way. A parent with neither its own manifest nor any child manifest is
181
+ * skipped exactly as before ("no readable pack.json"), and a child that
182
+ * DOES have a `pack.json` but fails validation is skipped individually, by
183
+ * its own (child) folder path.
184
+ */
185
+ export async function discoverPacks(folders, readFile, listDirectory, supportedEngine) {
186
+ const uniqueFolders = [...new Set(folders)];
187
+ const found = [];
188
+ const skipped = [];
189
+ for (const folder of uniqueFolders) {
190
+ const rawAttempt = await tryLoadPackAt(folder, readFile);
191
+ const attempt = rawAttempt.kind === 'found'
192
+ ? checkEngine(rawAttempt.pack, supportedEngine)
193
+ : rawAttempt;
194
+ if (attempt.kind === 'found') {
195
+ found.push(attempt.pack);
196
+ continue;
197
+ }
198
+ if (attempt.kind === 'invalid') {
199
+ skipped.push({
200
+ folder,
201
+ reason: attempt.reason,
202
+ ...(attempt.engineUnsupported
203
+ ? { engineUnsupported: attempt.engineUnsupported }
204
+ : {}),
205
+ });
206
+ continue;
207
+ }
208
+ // 'missing': this folder itself has no pack.json — try its immediate
209
+ // children, one level only.
210
+ let entries = [];
211
+ try {
212
+ entries = await listDirectory(folder);
213
+ }
214
+ catch {
215
+ entries = [];
216
+ }
217
+ let anyChildManifest = false;
218
+ for (const entry of entries.slice(0, MAX_CHILD_FOLDERS_PER_PARENT)) {
219
+ if (!entry.isDirectory)
220
+ continue;
221
+ const childFolder = joinPath(folder, entry.name);
222
+ const rawChildAttempt = await tryLoadPackAt(childFolder, readFile);
223
+ const childAttempt = rawChildAttempt.kind === 'found'
224
+ ? checkEngine(rawChildAttempt.pack, supportedEngine)
225
+ : rawChildAttempt;
226
+ if (childAttempt.kind === 'found') {
227
+ found.push(childAttempt.pack);
228
+ anyChildManifest = true;
229
+ }
230
+ else if (childAttempt.kind === 'invalid') {
231
+ skipped.push({
232
+ folder: childFolder,
233
+ reason: childAttempt.reason,
234
+ ...(childAttempt.engineUnsupported
235
+ ? { engineUnsupported: childAttempt.engineUnsupported }
236
+ : {}),
237
+ });
238
+ anyChildManifest = true;
239
+ }
240
+ // 'missing' for a child: an ordinary non-pack subfolder, not worth
241
+ // reporting individually.
242
+ }
243
+ if (!anyChildManifest) {
244
+ skipped.push({ folder, reason: 'no readable pack.json' });
245
+ }
246
+ }
247
+ const collisionNames = new Set(detectNamespaceCollisions(found.map((pack) => pack.manifest.name)).map((collision) => collision.namespace));
248
+ const packs = found.filter((pack) => !collisionNames.has(pack.manifest.name));
249
+ for (const pack of found) {
250
+ if (collisionNames.has(pack.manifest.name)) {
251
+ skipped.push({
252
+ folder: pack.folder,
253
+ reason: `pack namespace "${pack.manifest.name}" collides with another configured pack and was not installed`,
254
+ });
255
+ }
256
+ }
257
+ return {
258
+ packs,
259
+ collisions: [...collisionNames],
260
+ skipped,
261
+ };
262
+ }
263
+ /** The discovered packs' namespaces — what `resolveUses` (`@markii/pack`) checks a note's `uses:` declaration against. */
264
+ export function installedNamespaces(packs) {
265
+ return packs.map((pack) => pack.manifest.name);
266
+ }
@@ -0,0 +1,79 @@
1
+ import type { PackManifest } from '@markii/pack';
2
+ /** The minimal shape of one loaded pack this module needs — a structural subset of `./discover.ts`'s `DiscoveredPack`. */
3
+ export interface PackDiagnosticsPack {
4
+ readonly manifest: Pick<PackManifest, 'name' | 'engine' | 'components'>;
5
+ }
6
+ /** The minimal shape of one skipped folder this module needs — a structural subset of `./discover.ts`'s `SkippedPackFolder`. */
7
+ export interface PackDiagnosticsSkippedFolder {
8
+ readonly folder: string;
9
+ readonly reason: string;
10
+ }
11
+ export interface PackDiagnosticsContext {
12
+ /** Every validated, non-colliding discovered pack. */
13
+ readonly packs: readonly PackDiagnosticsPack[];
14
+ /** The renderer engine whose components the "Loaded pack" line counts. Absent counts every declared component. */
15
+ readonly engine?: string;
16
+ /** Configured folders that produced no usable pack, and why. */
17
+ readonly skipped: readonly PackDiagnosticsSkippedFolder[];
18
+ /** Already-formatted lines, one per relative pack-folder-setting entry (host-specific wording — see this module's top doc comment). Defaults to none. */
19
+ readonly relativeEntryLines?: readonly string[];
20
+ /** Already-formatted informational lines, one per pack whose prebuilt webview.js shadows component sources on disk (host-specific wording, since each host names its own build command). Informational, never a failure: shipping both is a supported state. */
21
+ readonly prebuiltShadowLines?: readonly string[];
22
+ /** Pack CSS authoring warnings against every built pack's emitted stylesheet. Warnings only, developer-facing. */
23
+ readonly cssWarnings: readonly string[];
24
+ /** One line per malformed pack registration, dropped rather than installed (`./pack-render-registry.ts`'s `BuildRenderRegistryResult.invalidReasons`). Omitted (or empty) contributes nothing — a host that never validates registrations on this side (e.g. VS Code's webview validates them separately, in the browser) simply has none to report yet. */
25
+ readonly invalidRegistrationReasons?: readonly string[];
26
+ /** Namespaces shared by two or more registered packs (`./pack-render-registry.ts`'s `BuildRenderRegistryResult.collisions`). When non-empty, contributes one summary line. */
27
+ readonly registrationCollisions?: readonly string[];
28
+ /** Composed directive names two DIFFERENT packs both claimed (`./pack-render-registry.ts`'s `BuildRenderRegistryResult.duplicateComposedNames`) — the first pack keeps the name, and each entry here names both the pack that kept it and the pack whose component was skipped. Omitted (or empty) contributes nothing; this is expected to stay empty under ordinary pack composition (see that module's doc comment). */
29
+ readonly duplicateComposedNames?: readonly {
30
+ readonly composedName: string;
31
+ readonly keptPack: string;
32
+ readonly skippedPack: string;
33
+ }[];
34
+ /**
35
+ * One entry per validated, non-colliding pack whose declared engine
36
+ * `@markii/react`'s `loadPack` cannot run (`./pack-render-registry.ts`'s
37
+ * `BuildRenderRegistryResult.droppedEngines`). Omitted (or
38
+ * empty) contributes nothing — the ordinary case where every registered
39
+ * pack's engine is supported.
40
+ */
41
+ readonly droppedEngines?: readonly {
42
+ name: string;
43
+ engine: string;
44
+ }[];
45
+ }
46
+ /** The one line for a folder `discoverPacks` could not turn into a usable pack. */
47
+ export declare function skippedPackFolderLine(entry: PackDiagnosticsSkippedFolder): string;
48
+ /**
49
+ * The full set of diagnostic lines for one pack-loading outcome: loaded
50
+ * packs first (the confirmation that the setting is working at all), then
51
+ * every skipped folder with its reason, then relative-entry lines, then
52
+ * prebuilt-shadow lines, then pack CSS lint warnings, then any
53
+ * invalid-registration, namespace-collision, or duplicate-composed-name
54
+ * lines. Empty when nothing is configured at all — the caller decides
55
+ * whether an empty result is worth writing anything to its own
56
+ * diagnostics surface.
57
+ */
58
+ export declare function formatPackDiagnosticLines(context: PackDiagnosticsContext): string[];
59
+ /** How many configured folders failed to produce a usable pack — what a preview's quiet marker counts. */
60
+ export declare function skippedPackCount(context: {
61
+ readonly skipped: readonly PackDiagnosticsSkippedFolder[];
62
+ }): number;
63
+ /**
64
+ * One informational line for a pack whose prebuilt `webview.js` shadows
65
+ * component sources still present in the same folder.
66
+ * The tail clause names how a user would rebuild the prebuilt artifact
67
+ * from those sources: VS Code's own Export Pack command, or, from
68
+ * Obsidian, VS Code's Export Pack command by name, since Obsidian has no
69
+ * build command of its own.
70
+ *
71
+ * Never a failure: shipping both the built artifact and its sources is a
72
+ * supported distribution shape. `rebuildInstruction` is the one thing that
73
+ * differs per host — a short clause completing "Edits to them take effect
74
+ * only after you delete webview.js, or ...".
75
+ */
76
+ export declare function prebuiltShadowLine(pack: {
77
+ readonly name: string;
78
+ readonly folder: string;
79
+ }, rebuildInstruction: string): string;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Formats a host's pack-loading outcome as plain text lines for that host's
3
+ * own diagnostics surface (AGENTS.md's "clean is not silent": every
4
+ * failure needs a full diagnostic somewhere a user can find it, not just a
5
+ * quiet marker in the preview).
6
+ *
7
+ * This module owns the STRUCTURAL wording — one line per loaded pack, one
8
+ * per skipped folder, the CSS-warning lines, and the pack-registration
9
+ * lines (invalid registration reasons and the namespace-collision line) —
10
+ * which is identical across every host. It deliberately does NOT format
11
+ * the relative-entry note or the prebuilt-shadow note: which setting a
12
+ * relative entry belongs to (VS Code's `markii.packs`, this plugin's
13
+ * device-local pack-folder list) and what "relative" means for that host (a
14
+ * different workspace window vs. a different vault) is host-specific
15
+ * knowledge, and naming a host's own "build pack for distribution" command
16
+ * in the shadow note is host-specific the same way, so each host formats
17
+ * those lines itself and passes them in already-rendered
18
+ * (`relativeEntryLines`/`prebuiltShadowLines` below) — see
19
+ * `apps/vscode/src/packs/pack-diagnostics.ts` and
20
+ * `apps/obsidian/src/packs/pack-diagnostics.ts` for each host's own wording
21
+ * and thin wrapper around this function.
22
+ */
23
+ import { packComponents, packComponentsForEngine } from '@markii/pack';
24
+ /** The one line for a folder `discoverPacks` could not turn into a usable pack. */
25
+ export function skippedPackFolderLine(entry) {
26
+ return `Skipped pack folder "${entry.folder}": ${entry.reason}`;
27
+ }
28
+ /** One line for each folder `discoverPacks`/a host's `loadPackContext` could not turn into a usable pack. */
29
+ function skippedLines(context) {
30
+ return context.skipped.map(skippedPackFolderLine);
31
+ }
32
+ /** One line for each pack that loaded successfully, naming what a user would want to confirm: its name, namespace, and how many components it registered. */
33
+ function loadedLines(context) {
34
+ return context.packs.map((pack) => {
35
+ const componentCount = context.engine === undefined
36
+ ? packComponents(pack.manifest).length
37
+ : packComponentsForEngine(pack.manifest, context.engine).length;
38
+ const plural = componentCount === 1 ? 'component' : 'components';
39
+ return `Loaded pack "${pack.manifest.name}" (namespace: ${pack.manifest.name}, ${String(componentCount)} ${plural})`;
40
+ });
41
+ }
42
+ /** The one summary line for a namespace collision among registered packs, or `[]` when there was none. */
43
+ function collisionLines(context) {
44
+ const collisions = context.registrationCollisions ?? [];
45
+ if (collisions.length === 0)
46
+ return [];
47
+ return [
48
+ `Installed packs share a namespace (${collisions.join(', ')}); none of them were installed.`,
49
+ ];
50
+ }
51
+ /** One line per composed directive name two different packs both claimed, naming the pack that kept it and the pack whose component was skipped — `[]` when there was none (the ordinary case). */
52
+ function duplicateComposedNameLines(context) {
53
+ const duplicates = context.duplicateComposedNames ?? [];
54
+ return duplicates.map((duplicate) => `Pack "${duplicate.skippedPack}"'s component composed to the directive name "${duplicate.composedName}", already claimed by pack "${duplicate.keptPack}"; the later component was skipped.`);
55
+ }
56
+ /** One line per pack dropped because this renderer cannot run its declared engine — `[]` when there was none. */
57
+ function droppedEngineLines(context) {
58
+ const dropped = context.droppedEngines ?? [];
59
+ return dropped.map((entry) => `Pack "${entry.name}" declares engine "${entry.engine}", which this renderer cannot run; it installed with zero components (every directive under its namespace falls back to the unknown-component box).`);
60
+ }
61
+ /**
62
+ * The full set of diagnostic lines for one pack-loading outcome: loaded
63
+ * packs first (the confirmation that the setting is working at all), then
64
+ * every skipped folder with its reason, then relative-entry lines, then
65
+ * prebuilt-shadow lines, then pack CSS lint warnings, then any
66
+ * invalid-registration, namespace-collision, or duplicate-composed-name
67
+ * lines. Empty when nothing is configured at all — the caller decides
68
+ * whether an empty result is worth writing anything to its own
69
+ * diagnostics surface.
70
+ */
71
+ export function formatPackDiagnosticLines(context) {
72
+ return [
73
+ ...loadedLines(context),
74
+ ...skippedLines(context),
75
+ ...(context.relativeEntryLines ?? []),
76
+ ...(context.prebuiltShadowLines ?? []),
77
+ ...context.cssWarnings,
78
+ ...(context.invalidRegistrationReasons ?? []),
79
+ ...collisionLines(context),
80
+ ...duplicateComposedNameLines(context),
81
+ ...droppedEngineLines(context),
82
+ ];
83
+ }
84
+ /** How many configured folders failed to produce a usable pack — what a preview's quiet marker counts. */
85
+ export function skippedPackCount(context) {
86
+ return context.skipped.length;
87
+ }
88
+ /**
89
+ * One informational line for a pack whose prebuilt `webview.js` shadows
90
+ * component sources still present in the same folder.
91
+ * The tail clause names how a user would rebuild the prebuilt artifact
92
+ * from those sources: VS Code's own Export Pack command, or, from
93
+ * Obsidian, VS Code's Export Pack command by name, since Obsidian has no
94
+ * build command of its own.
95
+ *
96
+ * Never a failure: shipping both the built artifact and its sources is a
97
+ * supported distribution shape. `rebuildInstruction` is the one thing that
98
+ * differs per host — a short clause completing "Edits to them take effect
99
+ * only after you delete webview.js, or ...".
100
+ */
101
+ export function prebuiltShadowLine(pack, rebuildInstruction) {
102
+ return `Pack "${pack.name}" is using its prebuilt webview.js, so the component sources in that folder are not compiled. Edits to them take effect only after you delete webview.js, or ${rebuildInstruction}.`;
103
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Resolves each entry in `packs` to an absolute path. `homeDir` is a plain
3
+ * parameter (not read via `os.homedir()` internally) so this stays testable
4
+ * against a fixed value instead of the real host's home directory; the real
5
+ * caller (each host's own `pack-context.ts`) passes `os.homedir()` itself.
6
+ * Omitted (or `undefined`) disables `~` expansion entirely — a `~` entry is
7
+ * then simply relative like any other.
8
+ *
9
+ * - A `~`/`~/...` entry is expanded against `homeDir` FIRST (`expandHome`)
10
+ * — this makes an absolute path pleasant to write, which matters because
11
+ * a relative entry can mean a different folder in every window/vault it
12
+ * happens to be open in, so an absolute (or `~`-relative) entry is the
13
+ * only spelling that means the same folder everywhere.
14
+ * - An entry that is (now) already absolute passes through UNCHANGED —
15
+ * never re-rooted, and never rejected for pointing outside `baseDir`,
16
+ * since an absolute path is the user explicitly naming a folder wherever
17
+ * it is (docs/packs.md: packs are installed into the application, not
18
+ * jailed the way a bundle's contents are).
19
+ * - A relative entry (still relative after `~` expansion) is joined
20
+ * against `baseDir` and NORMALIZED (`path.normalize` via `path.join`,
21
+ * which collapses `..` segments syntactically). A relative entry that
22
+ * climbs out of `baseDir` via `..` is intentionally still honored, for
23
+ * the same reason an absolute entry is: this setting is a user-authored
24
+ * trust decision, not a content-derived path that needs jailing — unlike
25
+ * a bundle's `scripts/`/`assets/` paths (`@markii/bundle`'s
26
+ * `normalizeBundlePath`), which DO get jailed because they come from
27
+ * potentially-hostile bundle content, not from the user directly editing
28
+ * their own settings.
29
+ * - `baseDir === undefined` (no workspace/vault open) leaves every relative
30
+ * entry UNRESOLVED (returned as-is, unjoined) rather than guessing a
31
+ * base — a relative entry with nothing open cannot mean anything, so the
32
+ * caller's later folder-read simply fails to find a `pack.json` there
33
+ * and `discoverPacks` quietly skips it, the same cleanliness posture as
34
+ * any other unreadable folder.
35
+ *
36
+ * Never throws — every input is a configuration string a user typed by
37
+ * hand, and a malformed one should degrade to "not found", not to a crash.
38
+ */
39
+ export declare function resolvePackPaths(packs: readonly string[], baseDir: string | undefined, homeDir?: string): string[];
40
+ /**
41
+ * The detection half of the relative-entry diagnostics: the ORIGINAL
42
+ * (unexpanded) entries in
43
+ * `packs` that are relative once `~` expansion is accounted for — i.e.
44
+ * exactly the entries `resolvePackPaths` resolves against `baseDir` rather
45
+ * than using as-is. A relative entry can silently mean a different folder
46
+ * in every window/vault it happens to be open in, so a caller surfaces
47
+ * these to its own diagnostics surface, in its own wording (each host
48
+ * names its setting and the ambiguity differently — see this module's top
49
+ * doc comment); `resolvePackPaths` keeps resolving them exactly as it
50
+ * always has: this function only ever adds a warning, never changes
51
+ * behavior.
52
+ */
53
+ export declare function relativePackEntries(packs: readonly string[], homeDir?: string): string[];