@loomweaver/plugin-sdk 0.7.2

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 (71) hide show
  1. package/README.md +11 -0
  2. package/package.json +45 -0
  3. package/src/index.d.ts +22 -0
  4. package/src/index.js +23 -0
  5. package/src/index.js.map +1 -0
  6. package/src/lib/auth.d.ts +60 -0
  7. package/src/lib/auth.js +35 -0
  8. package/src/lib/auth.js.map +1 -0
  9. package/src/lib/bar-item.d.ts +67 -0
  10. package/src/lib/bar-item.js +2 -0
  11. package/src/lib/bar-item.js.map +1 -0
  12. package/src/lib/button.d.ts +4 -0
  13. package/src/lib/button.js +2 -0
  14. package/src/lib/button.js.map +1 -0
  15. package/src/lib/capability.d.ts +38 -0
  16. package/src/lib/capability.js +32 -0
  17. package/src/lib/capability.js.map +1 -0
  18. package/src/lib/command.d.ts +199 -0
  19. package/src/lib/command.js +2 -0
  20. package/src/lib/command.js.map +1 -0
  21. package/src/lib/container-handle.d.ts +36 -0
  22. package/src/lib/container-handle.js +4 -0
  23. package/src/lib/container-handle.js.map +1 -0
  24. package/src/lib/content-route.d.ts +254 -0
  25. package/src/lib/content-route.js +2 -0
  26. package/src/lib/content-route.js.map +1 -0
  27. package/src/lib/contribution.d.ts +4 -0
  28. package/src/lib/contribution.js +2 -0
  29. package/src/lib/contribution.js.map +1 -0
  30. package/src/lib/dialog-ref.d.ts +25 -0
  31. package/src/lib/dialog-ref.js +37 -0
  32. package/src/lib/dialog-ref.js.map +1 -0
  33. package/src/lib/dialog.d.ts +105 -0
  34. package/src/lib/dialog.js +2 -0
  35. package/src/lib/dialog.js.map +1 -0
  36. package/src/lib/dirty-surface.d.ts +48 -0
  37. package/src/lib/dirty-surface.js +2 -0
  38. package/src/lib/dirty-surface.js.map +1 -0
  39. package/src/lib/menu.d.ts +48 -0
  40. package/src/lib/menu.js +2 -0
  41. package/src/lib/menu.js.map +1 -0
  42. package/src/lib/notification.d.ts +28 -0
  43. package/src/lib/notification.js +2 -0
  44. package/src/lib/notification.js.map +1 -0
  45. package/src/lib/pane-area.d.ts +29 -0
  46. package/src/lib/pane-area.js +2 -0
  47. package/src/lib/pane-area.js.map +1 -0
  48. package/src/lib/plugin-state.d.ts +52 -0
  49. package/src/lib/plugin-state.js +2 -0
  50. package/src/lib/plugin-state.js.map +1 -0
  51. package/src/lib/plugin.d.ts +259 -0
  52. package/src/lib/plugin.js +2 -0
  53. package/src/lib/plugin.js.map +1 -0
  54. package/src/lib/product-identity.d.ts +25 -0
  55. package/src/lib/product-identity.js +17 -0
  56. package/src/lib/product-identity.js.map +1 -0
  57. package/src/lib/rail-item.d.ts +61 -0
  58. package/src/lib/rail-item.js +2 -0
  59. package/src/lib/rail-item.js.map +1 -0
  60. package/src/lib/settings-model.d.ts +164 -0
  61. package/src/lib/settings-model.js +2 -0
  62. package/src/lib/settings-model.js.map +1 -0
  63. package/src/lib/surface.d.ts +220 -0
  64. package/src/lib/surface.js +2 -0
  65. package/src/lib/surface.js.map +1 -0
  66. package/src/lib/view-state.d.ts +38 -0
  67. package/src/lib/view-state.js +9 -0
  68. package/src/lib/view-state.js.map +1 -0
  69. package/src/lib/view.d.ts +103 -0
  70. package/src/lib/view.js +2 -0
  71. package/src/lib/view.js.map +1 -0
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # plugin-sdk
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Building
6
+
7
+ Run `nx build plugin-sdk` to build the library.
8
+
9
+ ## Running unit tests
10
+
11
+ Run `nx test plugin-sdk` to execute the unit tests via [Jest](https://jestjs.io).
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@loomweaver/plugin-sdk",
3
+ "version": "0.7.2",
4
+ "description": "LoomWeaver plugin contract: the public SDK a weaver depends on — ProductIdentity, Command, PluginContext and the host UI vocabulary.",
5
+ "keywords": [
6
+ "loomweaver",
7
+ "plugin",
8
+ "sdk",
9
+ "plugin-contract",
10
+ "angular",
11
+ "extensibility",
12
+ "plugin-platform"
13
+ ],
14
+ "license": "Apache-2.0",
15
+ "author": "Norbert Rosenwinkel (https://loomweaver.dev)",
16
+ "type": "module",
17
+ "main": "./src/index.js",
18
+ "module": "./src/index.js",
19
+ "types": "./src/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./src/index.d.ts",
23
+ "default": "./src/index.js"
24
+ },
25
+ "./package.json": "./package.json"
26
+ },
27
+ "dependencies": {
28
+ "tslib": "^2.3.0"
29
+ },
30
+ "peerDependencies": {
31
+ "@angular/core": "^22.0.0"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "homepage": "https://loomweaver.dev",
37
+ "bugs": {
38
+ "url": "https://github.com/yesbert/loomweaver/issues"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/yesbert/loomweaver.git",
43
+ "directory": "platform/libs/core/plugin-sdk"
44
+ }
45
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ export * from './lib/product-identity.js';
2
+ export * from './lib/contribution.js';
3
+ export * from './lib/capability.js';
4
+ export * from './lib/auth.js';
5
+ export * from './lib/menu.js';
6
+ export * from './lib/command.js';
7
+ export * from './lib/button.js';
8
+ export * from './lib/view.js';
9
+ export * from './lib/view-state.js';
10
+ export * from './lib/plugin-state.js';
11
+ export * from './lib/dirty-surface.js';
12
+ export * from './lib/pane-area.js';
13
+ export * from './lib/container-handle.js';
14
+ export * from './lib/content-route.js';
15
+ export * from './lib/surface.js';
16
+ export * from './lib/bar-item.js';
17
+ export * from './lib/rail-item.js';
18
+ export * from './lib/settings-model.js';
19
+ export * from './lib/dialog-ref.js';
20
+ export * from './lib/dialog.js';
21
+ export * from './lib/notification.js';
22
+ export * from './lib/plugin.js';
package/src/index.js ADDED
@@ -0,0 +1,23 @@
1
+ export * from './lib/product-identity.js';
2
+ export * from './lib/contribution.js';
3
+ export * from './lib/capability.js';
4
+ export * from './lib/auth.js';
5
+ export * from './lib/menu.js';
6
+ export * from './lib/command.js';
7
+ export * from './lib/button.js';
8
+ export * from './lib/view.js';
9
+ export * from './lib/view-state.js';
10
+ export * from './lib/plugin-state.js';
11
+ export * from './lib/dirty-surface.js';
12
+ export * from './lib/pane-area.js';
13
+ export * from './lib/container-handle.js';
14
+ export * from './lib/content-route.js';
15
+ export * from './lib/surface.js';
16
+ export * from './lib/bar-item.js';
17
+ export * from './lib/rail-item.js';
18
+ export * from './lib/settings-model.js';
19
+ export * from './lib/dialog-ref.js';
20
+ export * from './lib/dialog.js';
21
+ export * from './lib/notification.js';
22
+ export * from './lib/plugin.js';
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../libs/core/plugin-sdk/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Auth-aware, declarative access gating. LoomWeaver owns **none** of authentication —
3
+ * login, session, tokens, IdP integration live in the product. The platform only *reacts* to a
4
+ * session snapshot a distribution supplies, so chrome can hide/disable itself by login state and
5
+ * roles. Roles/claims are **opaque strings**: the platform matches, never interprets.
6
+ *
7
+ * Client-side gating is **presentation, not security** — the real enforcement is server-side (the
8
+ * product BFF rejects unauthorized calls). Never treat a hidden control as a boundary.
9
+ */
10
+ /** A user/session snapshot the host reacts to. Provider-neutral (OIDC/custom/anything reduces here). */
11
+ export interface AuthSnapshot {
12
+ /** Whether someone is signed in. */
13
+ readonly authenticated: boolean;
14
+ /** Opaque role tokens held by the current principal (empty when anonymous). */
15
+ readonly roles: readonly string[];
16
+ /**
17
+ * Free-form claim bag; values are strings or string lists. It stays in the distribution's own
18
+ * code: **no plugin receives it** — a plugin permitted the session is told `authenticated` and
19
+ * `roles`, and nothing else — and **no access requirement evaluates it**, since the host matches
20
+ * on `authenticated` and `roles` only. Read it from your own composition; do not expect it to
21
+ * reach a plugin or to gate anything.
22
+ */
23
+ readonly claims: Readonly<Record<string, string | readonly string[]>>;
24
+ /**
25
+ * Optional stable subject id — the **identity anchor**: the shell's identity-change
26
+ * policy (`provideAuthSource(..., { onIdentityChange })`) compares it across snapshots, and an
27
+ * identity-scoped settings store namespaces per-user state by it. Set it whenever the product
28
+ * can name the signed-in principal; encode the tenant into it if tenant switches should count
29
+ * as identity changes. Omit it and both features stay inert.
30
+ */
31
+ readonly subject?: string;
32
+ /** Optional, display-only human name. */
33
+ readonly displayName?: string;
34
+ }
35
+ /** The anonymous baseline — the bare-platform default (nobody signed in). */
36
+ export declare const ANONYMOUS: AuthSnapshot;
37
+ /**
38
+ * How a contribution reacts to the auth state. Intentionally **coarse** — no expression parser
39
+ * (consistent with the `when` filter). Generic claim matching is the documented extension
40
+ * once a real case appears (YAGNI).
41
+ */
42
+ export interface AccessRequirement {
43
+ /** Require a specific signed-in state (`true` = must be signed in, `false` = only anonymous). */
44
+ readonly authenticated?: boolean;
45
+ /** Require at least one of these roles. */
46
+ readonly anyRole?: readonly string[];
47
+ /** Require all of these roles. */
48
+ readonly allRoles?: readonly string[];
49
+ /**
50
+ * Effect on a chrome item when the requirement is **unmet**: `hide` (default) removes it, `disable`
51
+ * keeps it visible but inert. Routes/commands block on invoke regardless of this flag.
52
+ */
53
+ readonly mode?: 'hide' | 'disable';
54
+ }
55
+ /** True when `snapshot` satisfies `access`. An undefined requirement is always allowed. */
56
+ export declare function meetsAccess(access: AccessRequirement | undefined, snapshot: AuthSnapshot): boolean;
57
+ /** Whether a chrome item should render at all: unmet + `hide` (the default) → not visible. */
58
+ export declare function isAccessVisible(access: AccessRequirement | undefined, snapshot: AuthSnapshot): boolean;
59
+ /** Whether a rendered chrome item should be disabled: unmet + `disable`. */
60
+ export declare function isAccessDisabled(access: AccessRequirement | undefined, snapshot: AuthSnapshot): boolean;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Auth-aware, declarative access gating. LoomWeaver owns **none** of authentication —
3
+ * login, session, tokens, IdP integration live in the product. The platform only *reacts* to a
4
+ * session snapshot a distribution supplies, so chrome can hide/disable itself by login state and
5
+ * roles. Roles/claims are **opaque strings**: the platform matches, never interprets.
6
+ *
7
+ * Client-side gating is **presentation, not security** — the real enforcement is server-side (the
8
+ * product BFF rejects unauthorized calls). Never treat a hidden control as a boundary.
9
+ */
10
+ /** The anonymous baseline — the bare-platform default (nobody signed in). */
11
+ export const ANONYMOUS = { authenticated: false, roles: [], claims: {} };
12
+ /** True when `snapshot` satisfies `access`. An undefined requirement is always allowed. */
13
+ export function meetsAccess(access, snapshot) {
14
+ var _a, _b;
15
+ if (!access)
16
+ return true;
17
+ if (access.authenticated !== undefined && access.authenticated !== snapshot.authenticated) {
18
+ return false;
19
+ }
20
+ const held = new Set(snapshot.roles);
21
+ if (((_a = access.anyRole) === null || _a === void 0 ? void 0 : _a.length) && !access.anyRole.some((role) => held.has(role)))
22
+ return false;
23
+ if (((_b = access.allRoles) === null || _b === void 0 ? void 0 : _b.length) && !access.allRoles.every((role) => held.has(role)))
24
+ return false;
25
+ return true;
26
+ }
27
+ /** Whether a chrome item should render at all: unmet + `hide` (the default) → not visible. */
28
+ export function isAccessVisible(access, snapshot) {
29
+ return meetsAccess(access, snapshot) || (access === null || access === void 0 ? void 0 : access.mode) === 'disable';
30
+ }
31
+ /** Whether a rendered chrome item should be disabled: unmet + `disable`. */
32
+ export function isAccessDisabled(access, snapshot) {
33
+ return !!access && access.mode === 'disable' && !meetsAccess(access, snapshot);
34
+ }
35
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../../../libs/core/plugin-sdk/src/lib/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA4BH,6EAA6E;AAC7E,MAAM,CAAC,MAAM,SAAS,GAAiB,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAqBvF,2FAA2F;AAC3F,MAAM,UAAU,WAAW,CACzB,MAAqC,EACrC,QAAsB;;IAEtB,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,MAAM,CAAC,aAAa,KAAK,SAAS,IAAI,MAAM,CAAC,aAAa,KAAK,QAAQ,CAAC,aAAa,EAAE,CAAC;QAC1F,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,CAAA,MAAA,MAAM,CAAC,OAAO,0CAAE,MAAM,KAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3F,IAAI,CAAA,MAAA,MAAM,CAAC,QAAQ,0CAAE,MAAM,KAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9F,OAAO,IAAI,CAAC;AACd,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,eAAe,CAC7B,MAAqC,EACrC,QAAsB;IAEtB,OAAO,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,IAAI,MAAK,SAAS,CAAC;AACrE,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,gBAAgB,CAC9B,MAAqC,EACrC,QAAsB;IAEtB,OAAO,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AACjF,CAAC"}
@@ -0,0 +1,67 @@
1
+ import { Type } from '@angular/core';
2
+ import { AccessRequirement } from './auth.js';
3
+ /** Bar slots. */
4
+ export type BarSlot = 'start' | 'center' | 'end';
5
+ interface BarItemBase {
6
+ /** Stable id — same id overrides in place (last wins); used for ordering and removal. */
7
+ readonly id: string;
8
+ /** Target Bar region id (e.g. the top bar or a bottom status bar). */
9
+ readonly bar: string;
10
+ /** Which Bar slot the item renders in. */
11
+ readonly slot: BarSlot;
12
+ /** Lower renders first within a slot (default 0). */
13
+ readonly order?: number;
14
+ /**
15
+ * Declarative auth gating: the host hides (default) or, for a {@link BarButtonItem},
16
+ * disables this item when the current session does not meet the requirement. `disable` mode only
17
+ * applies to the host-drawn button — a {@link BarComponentItem} owns its own cell, so it is only
18
+ * ever hidden. Presentation only; real enforcement is server-side. Omit for an item everyone sees.
19
+ */
20
+ readonly access?: AccessRequirement;
21
+ }
22
+ /** A bar item that renders a plugin-provided component (full control over the cell). */
23
+ export interface BarComponentItem extends BarItemBase {
24
+ /** Component the host renders in the slot. */
25
+ readonly component: Type<unknown>;
26
+ }
27
+ /**
28
+ * A declarative bar button — the host renders the button **and** its tooltip from data
29
+ * (host offering). Lets a plugin add a status-/top-bar button without authoring
30
+ * a component or importing `<lw-tooltip>`.
31
+ */
32
+ export interface BarButtonItem extends BarItemBase {
33
+ /** Icon name shown in the button (optional if a label is given). */
34
+ readonly icon?: string;
35
+ /** Transloco key/literal for a visible text label (optional). */
36
+ readonly label?: string;
37
+ /** Transloco key/literal for the tooltip; falls back to {@link label}. */
38
+ readonly tooltip?: string;
39
+ /**
40
+ * Id of a menu slot to open as this button's **context menu** on right-click — region-agnostic:
41
+ * the host wires the right-click uniformly and passes a serialisable context (`{ targetKind, id, region }`).
42
+ * Contribute items to the slot with `ctx.registerMenuItem({ menu, … })`. Omit for no context menu.
43
+ */
44
+ readonly menu?: string;
45
+ /**
46
+ * Id of a registered {@link Command} this button triggers. Provide this **or** {@link run}; when
47
+ * set, the host runs that command (so a keybinding/palette can share the same behaviour).
48
+ */
49
+ readonly command?: string;
50
+ /**
51
+ * Show the {@link command}'s keyboard shortcut hint next to the label, OS-correct (⌘K on macOS,
52
+ * Ctrl+K elsewhere) — the same affordance a menu entry has. Only takes effect when {@link command}
53
+ * names a command that declares a `shortcut`; ignored otherwise. Opt-in because a bar is tighter
54
+ * than a menu. Default off.
55
+ */
56
+ readonly showShortcut?: boolean;
57
+ /**
58
+ * Inline behaviour, for a button that is not backed by a registered command. May be async; the
59
+ * host fires it fire-and-forget. Typed `() => void` so a one-expression arrow whose handler
60
+ * happens to return a value (e.g. `() => ctx.ui.openSettings()`) still assigns — the return is
61
+ * ignored either way.
62
+ */
63
+ run?(): void;
64
+ }
65
+ /** A control the host renders into a slot of a Bar region. */
66
+ export type BarItem = BarComponentItem | BarButtonItem;
67
+ export {};
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=bar-item.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bar-item.js","sourceRoot":"","sources":["../../../../../../libs/core/plugin-sdk/src/lib/bar-item.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ /** Visual weight of a button. */
2
+ export type LwButtonVariant = 'primary' | 'default' | 'success' | 'danger' | 'warning' | 'info' | 'ghost';
3
+ /** Button size; `md` is the default, `sm` for dense chrome (toasts, footers). */
4
+ export type LwButtonSize = 'sm' | 'md';
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button.js","sourceRoot":"","sources":["../../../../../../libs/core/plugin-sdk/src/lib/button.ts"],"names":[],"mappings":""}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Every coarse capability, in canonical display order. The single source of truth — {@link Capability}
3
+ * is derived from it so the type and the list can never drift, and a host surface (e.g. the built-in
4
+ * permissions settings) can iterate them.
5
+ */
6
+ export declare const CAPABILITIES: readonly ["contributions", "ui", "host", "navigation", "session", "theme", "automation"];
7
+ /**
8
+ * Coarse capabilities a plugin can hold — capability injection, **default-deny**: a plugin gets
9
+ * nothing it was not granted. Granularity is intentionally coarse first; a scope
10
+ * can split into finer ones later without changing the model. Each names a slice of the `ctx`:
11
+ *
12
+ * - `contributions` — register contributions (`ctx.registerSurface/Command/BarItem/RailItem/
13
+ * SettingsSection/MenuItem`, `ctx.contributeIcons`).
14
+ * - `ui` — use the host UI services (`ctx.ui.*`: dialogs, toasts, settings, context menus).
15
+ * - `host` — read host facts (`ctx.host`: version, update state).
16
+ * - `navigation` — drive and read the content area (`ctx.navigateContent/openContentTab/
17
+ * closeContentTab/keepContentTab/pinContentTab/unpinContentTab/revealSurface`
18
+ * and the read-side `ctx.activeContent`).
19
+ * - `session` — read session facts (`ctx.session`: login state + roles) for self-gating.
20
+ * - `theme` — contribute design tokens that re-color the whole app (`ctx.contributeTheme`).
21
+ * - `automation` — run actions other plugins contributed (`ctx.invokeCommand`,
22
+ * `ctx.invocableCommands`). A plugin reaches its own commands without it.
23
+ */
24
+ export type Capability = (typeof CAPABILITIES)[number];
25
+ /**
26
+ * Thrown when a plugin uses a `ctx` surface it was not granted (default-deny). A curated-v1
27
+ * misconfiguration (the distribution forgot to grant a capability the plugin needs) surfaces
28
+ * loudly here rather than as a silent no-op — part of the `ctx`-boundary error taxonomy.
29
+ */
30
+ export declare class CapabilityError extends Error {
31
+ readonly capability: Capability;
32
+ readonly pluginId: string;
33
+ /** Why this particular call needed the capability, when that is not obvious from the member alone. */
34
+ readonly reason?: string | undefined;
35
+ constructor(capability: Capability, pluginId: string,
36
+ /** Why this particular call needed the capability, when that is not obvious from the member alone. */
37
+ reason?: string | undefined);
38
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Every coarse capability, in canonical display order. The single source of truth — {@link Capability}
3
+ * is derived from it so the type and the list can never drift, and a host surface (e.g. the built-in
4
+ * permissions settings) can iterate them.
5
+ */
6
+ export const CAPABILITIES = [
7
+ 'contributions',
8
+ 'ui',
9
+ 'host',
10
+ 'navigation',
11
+ 'session',
12
+ 'theme',
13
+ 'automation',
14
+ ];
15
+ /**
16
+ * Thrown when a plugin uses a `ctx` surface it was not granted (default-deny). A curated-v1
17
+ * misconfiguration (the distribution forgot to grant a capability the plugin needs) surfaces
18
+ * loudly here rather than as a silent no-op — part of the `ctx`-boundary error taxonomy.
19
+ */
20
+ export class CapabilityError extends Error {
21
+ constructor(capability, pluginId,
22
+ /** Why this particular call needed the capability, when that is not obvious from the member alone. */
23
+ reason) {
24
+ super(`Plugin "${pluginId}" is missing the "${capability}" capability (default-deny).` +
25
+ (reason ? ` ${reason}` : ''));
26
+ this.capability = capability;
27
+ this.pluginId = pluginId;
28
+ this.reason = reason;
29
+ this.name = 'CapabilityError';
30
+ }
31
+ }
32
+ //# sourceMappingURL=capability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability.js","sourceRoot":"","sources":["../../../../../../libs/core/plugin-sdk/src/lib/capability.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,eAAe;IACf,IAAI;IACJ,MAAM;IACN,YAAY;IACZ,SAAS;IACT,OAAO;IACP,YAAY;CACJ,CAAC;AAqBX;;;;GAIG;AACH,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,YACW,UAAsB,EACtB,QAAgB;IACzB,sGAAsG;IAC7F,MAAe;QAExB,KAAK,CACH,WAAW,QAAQ,qBAAqB,UAAU,8BAA8B;YAC9E,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAC/B,CAAC;QARO,eAAU,GAAV,UAAU,CAAY;QACtB,aAAQ,GAAR,QAAQ,CAAQ;QAEhB,WAAM,GAAN,MAAM,CAAS;QAMxB,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF"}
@@ -0,0 +1,199 @@
1
+ import { AccessRequirement } from './auth.js';
2
+ import { MenuContext } from './menu.js';
3
+ /** A single value an argument or an answer can carry across any plugin boundary. */
4
+ export type CommandScalar = string | number | boolean;
5
+ /** What one declared argument may be given: a single value, or a list of them. */
6
+ export type CommandArgumentValue = CommandScalar | readonly CommandScalar[];
7
+ /** The arguments an invocation supplies, keyed by {@link CommandArgumentBase.name}. */
8
+ export type CommandArguments = Readonly<Record<string, CommandArgumentValue>>;
9
+ /**
10
+ * What a command may answer with — plain data, because an answer crosses the same boundary its
11
+ * arguments came from and anything else would arrive stripped of what it was.
12
+ */
13
+ export type CommandAnswer = CommandScalar | null | readonly CommandAnswer[] | {
14
+ readonly [key: string]: CommandAnswer;
15
+ };
16
+ /** What every declared argument carries, whatever kind of value it takes. */
17
+ export interface CommandArgumentBase {
18
+ /** The name the invocation keys this argument by. */
19
+ readonly name: string;
20
+ /**
21
+ * What this argument means, in prose — a Transloco key or a literal. Written for something
22
+ * *choosing* a value, not for a control labelling one, so say what it selects and what a sensible
23
+ * value looks like.
24
+ */
25
+ readonly description: string;
26
+ /** Whether an invocation must supply it. Omit for an optional argument. */
27
+ readonly required?: boolean;
28
+ /** Take a list of this kind rather than a single value. Omit for a single value. */
29
+ readonly list?: boolean;
30
+ }
31
+ /** An argument taking a free value of one of the three plain kinds. */
32
+ export interface SimpleCommandArgument extends CommandArgumentBase {
33
+ readonly kind: 'text' | 'number' | 'boolean';
34
+ }
35
+ /** An argument taking one of a fixed set of strings. */
36
+ export interface ChoiceCommandArgument extends CommandArgumentBase {
37
+ readonly kind: 'choice';
38
+ /** The values this argument accepts; anything else is refused before the command runs. */
39
+ readonly choices: readonly string[];
40
+ }
41
+ /**
42
+ * One argument a command accepts. The set of kinds is closed on purpose: a caller has to be able to
43
+ * describe a command to something that has never seen it, and a closed set makes a wrong declaration
44
+ * a compile error instead of a silent no-op. Widening it later is additive; narrowing it would not be.
45
+ */
46
+ export type CommandArgument = SimpleCommandArgument | ChoiceCommandArgument;
47
+ /**
48
+ * Why an invocation was refused. A refusal is not a failure: the command did not run.
49
+ *
50
+ * `unavailable` is deliberately one answer for several situations — no such command, not open to a
51
+ * foreign caller, the session does not meet its access requirement, the window does not host it, the
52
+ * calling plugin was not granted the capability. Telling them apart would let a caller map what is
53
+ * installed by invoking ids and reading the reason back.
54
+ */
55
+ export type CommandRefusalReason = 'unavailable' | 'invalid-arguments' | 'too-deep';
56
+ /** The command ran; `value` is present where the command declares {@link Command.answers}. */
57
+ export interface CommandAnswered {
58
+ readonly outcome: 'answered';
59
+ readonly value?: CommandAnswer;
60
+ }
61
+ /** The command did not run. */
62
+ export interface CommandRefused {
63
+ readonly outcome: 'refused';
64
+ readonly reason: CommandRefusalReason;
65
+ /** A developer-facing explanation; never the place to show a user a message. */
66
+ readonly message: string;
67
+ }
68
+ /** The command ran and threw, or its asynchronous work rejected. */
69
+ export interface CommandFailed {
70
+ readonly outcome: 'failed';
71
+ readonly message: string;
72
+ }
73
+ /**
74
+ * What an invocation answers with. The three cases are distinct because a caller has to tell
75
+ * "you may not" from "it broke" from "here is your answer", and none of the three may be presented
76
+ * as either of the others.
77
+ */
78
+ export type CommandOutcome = CommandAnswered | CommandRefused | CommandFailed;
79
+ /**
80
+ * A command as offered to a caller that may invoke it — the workbench's own account of what it can
81
+ * be asked to do. Every text is already resolved to the active language, because a caller outside the
82
+ * application cannot reach the translation bundles and a raw key would be useless to it.
83
+ */
84
+ export interface InvocableCommand {
85
+ readonly id: string;
86
+ readonly title: string;
87
+ readonly description?: string;
88
+ readonly arguments?: readonly CommandArgument[];
89
+ readonly answers?: string;
90
+ }
91
+ /**
92
+ * A named, invocable action.
93
+ * A command is the stable anchor that a keybinding, a command-palette entry, and a UI item
94
+ * (rail/bar/view-action/menu) all point at by {@link Command.id}: one behaviour, many triggers.
95
+ *
96
+ * Register via `ctx.registerCommand(...)`; a UI item references it with `command: <id>` instead
97
+ * of carrying its own inline `run()`.
98
+ */
99
+ export interface Command {
100
+ /** Stable, namespaced id (e.g. "testbed.reset") — what triggers reference. */
101
+ readonly id: string;
102
+ /** Transloco key (or literal) for the command's label (command palette, menus). */
103
+ readonly title: string;
104
+ /** Optional icon name — resolved by the host icon registry (a plain string). */
105
+ readonly icon?: string;
106
+ /**
107
+ * Optional default keyboard shortcut — a chord like `"mod+enter"` or `"mod+shift+p"`. Tokens are
108
+ * case-insensitive and `+`-joined: modifiers `mod` (⌘ on macOS, Ctrl elsewhere), `ctrl`, `meta`/
109
+ * `cmd`, `alt`/`option`, `shift`, then one key (`k`, `enter`, `escape`, …). The host binds it; user
110
+ * rebinding is deferred.
111
+ */
112
+ readonly shortcut?: string;
113
+ /**
114
+ * Declarative auth gating: the host **blocks** this command when the current session
115
+ * does not meet the requirement — every trigger flows through one seam, so the keybinding no-ops,
116
+ * the command palette omits it, and a UI item bound to it does nothing. A command is invocable or
117
+ * not, so `mode` is ignored here. Presentation only — real enforcement is server-side. Omit for a
118
+ * command everyone can run.
119
+ */
120
+ readonly access?: AccessRequirement;
121
+ /**
122
+ * Hide this command from the command palette. Set it on a **context-only** command — one whose
123
+ * {@link Command.run} needs a {@link MenuContext} (a tab or view id) that only a menu supplies, so
124
+ * the palette (which invokes with no context) could offer nothing but a no-op. Menu items and
125
+ * keybindings still invoke it normally. Omit for a command the palette should list.
126
+ */
127
+ readonly paletteHidden?: boolean;
128
+ /**
129
+ * Offer this command in a **pop-out window** as well. Commands are main-window-only by
130
+ * default: a pop-out shows exactly one surface, with no tab strip, rail or sidebar, and is a viewer
131
+ * onto that surface rather than half the application. Without this flag the palette omits the
132
+ * command there, its keybinding no-ops and a UI item bound to it does nothing — the same one seam
133
+ * `access` flows through.
134
+ *
135
+ * The default is the quiet one on purpose: a command *missing* from a pop-out is a small
136
+ * annoyance, while one that does something surprising in a detached window is the larger failure,
137
+ * and the shell cannot tell the two apart for a command it did not write.
138
+ *
139
+ * Set it on what genuinely belongs beside a single surface — an about dialog, a theme toggle, an
140
+ * action on the surface's own data. Leave it off for anything that reaches for chrome a pop-out
141
+ * does not have: navigating the content area (which the shell refuses there anyway, since it would
142
+ * take the window out of its `/popout/…` address), opening or revealing a tab, focusing a docked
143
+ * view, changing the layout.
144
+ */
145
+ readonly popout?: boolean;
146
+ /**
147
+ * What this command *does*, in prose — a Transloco key or a literal. Distinct from
148
+ * {@link Command.title}, which labels a control: a title is read beside an icon by someone who can
149
+ * already see where they are, a description is read by something deciding between actions it has
150
+ * never seen. Omit it and the command has none; the title is never substituted, because a label is
151
+ * not an explanation.
152
+ *
153
+ * A command that sets {@link Command.callable} without one is unusable to the very caller it opened
154
+ * itself to, and the manifest validator says so.
155
+ */
156
+ readonly description?: string;
157
+ /**
158
+ * The arguments this command accepts. The host checks an invocation against them before the
159
+ * command runs — a missing required argument, a value of the wrong kind, or a choice outside the
160
+ * declared set is refused rather than reaching `run`. That check is for discovery, not for safety:
161
+ * validate your own inputs as you would without it.
162
+ */
163
+ readonly arguments?: readonly CommandArgument[];
164
+ /**
165
+ * What this command answers with, in prose — a Transloco key or a literal. Declaring it is what
166
+ * makes {@link Command.run}'s return value the invocation's answer; without it an invocation
167
+ * succeeds carrying nothing, whatever `run` happened to return.
168
+ */
169
+ readonly answers?: string;
170
+ /**
171
+ * Let a caller **other than this plugin** invoke this command by its id. Omitted, it cannot be
172
+ * reached that way by any route and is absent from everything that lists what such a caller may
173
+ * run; the plugin that registered it always reaches its own regardless.
174
+ *
175
+ * The default is the quiet one on purpose, exactly as {@link Command.popout} is: a command
176
+ * *missing* from what an automated caller can reach is a small annoyance, while one that does
177
+ * something surprising because something other than the user triggered it is the larger failure,
178
+ * and the shell cannot tell the two apart for a command it did not write.
179
+ *
180
+ * Opening a command widens nothing else. It still runs only where its {@link Command.access}, its
181
+ * {@link Command.popout} declaration and the caller's own granted capabilities already allow, so a
182
+ * caller can never reach through it to something the user could not have triggered themselves.
183
+ */
184
+ readonly callable?: boolean;
185
+ /**
186
+ * The behaviour. May be async; the host fires it and reports a failure rather than throwing.
187
+ * The return type is `unknown` so that a one-expression arrow handler still assigns whatever it
188
+ * returns; the host passes a value back to a caller only where {@link Command.answers} is declared,
189
+ * and only where the value is plain data.
190
+ *
191
+ * Receives an optional {@link MenuContext} when invoked from a menu — e.g. the tab context
192
+ * menu passes `{ tabId, … }` so `shell.tab.closeOthers` knows which tab. Triggers without a context
193
+ * (keybinding, palette, rail/bar item) call it with none; `run()` and `run(context)` are both valid.
194
+ *
195
+ * `args` holds what an invocation by id supplied, already checked against
196
+ * {@link Command.arguments}. A trigger the user drove supplies none.
197
+ */
198
+ run(context?: MenuContext, args?: CommandArguments): unknown;
199
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.js","sourceRoot":"","sources":["../../../../../../libs/core/plugin-sdk/src/lib/command.ts"],"names":[],"mappings":""}
@@ -0,0 +1,36 @@
1
+ import { InjectionToken } from '@angular/core';
2
+ /**
3
+ * What a container child can ask of the container it sits in. Injected by a child
4
+ * surface, so that a list child can open the item a row stands for in a sibling pane.
5
+ *
6
+ * It is a host operation rather than a navigation on purpose. A container tab may sit in a split
7
+ * pane or in a pop-out, where it holds no browser address at all, and a list whose rows only work
8
+ * while its container happens to hold the URL would not be a list. While the container *does* hold
9
+ * the address, the URL follows and shows the focused child; elsewhere the child opens just the same
10
+ * and the address simply does not express it.
11
+ *
12
+ * Only a **docked** surface mounted inside a container receives one; anywhere else the token resolves
13
+ * to `null`, so a surface that can appear in both places checks before it calls.
14
+ */
15
+ export interface ContainerHandle {
16
+ /**
17
+ * Open a child at an address inside this container, or focus it where it already is. The path is
18
+ * the child's declared segment with its values filled in — `open('item/42')` for a child declared
19
+ * as `{ surface: 'app.item', segment: 'item/:itemId' }`.
20
+ *
21
+ * It lands in the pane the container declared empty (`{ tabs: [] }`), which is what declaring one
22
+ * says; without such a pane it lands in the pane that currently carries the container's pointer.
23
+ *
24
+ * Give the tab a label, or several open items all read as the child surface's own title.
25
+ */
26
+ open(path: string, label?: ContainerTabLabel): void;
27
+ }
28
+ /** How an opened child's tab is labelled — the same fields a content tab carries. */
29
+ export interface ContainerTabLabel {
30
+ /** Transloco key, or a literal when {@link titleIsLiteral} is set. */
31
+ readonly title?: string;
32
+ readonly titleIsLiteral?: boolean;
33
+ readonly icon?: string;
34
+ }
35
+ /** DI token for the {@link ContainerHandle}; `null` outside a container. */
36
+ export declare const CONTAINER_HANDLE: InjectionToken<ContainerHandle | null>;
@@ -0,0 +1,4 @@
1
+ import { InjectionToken } from '@angular/core';
2
+ /** DI token for the {@link ContainerHandle}; `null` outside a container. */
3
+ export const CONTAINER_HANDLE = new InjectionToken('lw.container-handle', { factory: () => null });
4
+ //# sourceMappingURL=container-handle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-handle.js","sourceRoot":"","sources":["../../../../../../libs/core/plugin-sdk/src/lib/container-handle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAqC/C,4EAA4E;AAC5E,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,cAAc,CAChD,qBAAqB,EACrB,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CACxB,CAAC"}