@mutka-explorer/module 1.0.0-rc.2 → 1.0.0-rc.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 (4) hide show
  1. package/README.md +19 -13
  2. package/index.d.ts +22 -10
  3. package/index.js +7 -0
  4. package/package.json +7 -3
package/README.md CHANGED
@@ -1,15 +1,18 @@
1
1
  # @mutka-explorer/module
2
2
 
3
- TypeScript types for authoring [Mutka](https://github.com/ilianAZZ/mutka) modules.
3
+ Types + the `defineModule()` helper for authoring [Mutka](https://github.com/ilianAZZ/mutka) modules.
4
4
 
5
5
  A Mutka module is a single self-contained ESM file that
6
- `export default`s a module definition. It **imports nothing at runtime** it
7
- reaches the system only through the `host` object passed to `setup(host)`, and
8
- every `host.*` call is checked against the permissions it declares.
6
+ `export default`s a module definition. It reaches the system only through the
7
+ `host` object passed to `setup(host)`, and every `host.*` call is checked against
8
+ the permissions it declares.
9
9
 
10
- This package ships **only type definitions** (`.d.ts`, zero runtime code). You
11
- `import type` from it, TypeScript erases the import at compile time, and your
12
- built `index.js` stays self-containedexactly what Mutka loads.
10
+ This package is **types plus one tiny runtime export**: `defineModule`, an identity
11
+ function (`def => def`). Everything else is types, erased at compile time. The only
12
+ reason `defineModule` exists at runtime is type inference it captures your
13
+ `commands[].id`s so `host.onCommand` only accepts ids you declared (a typo or stale
14
+ id is a compile error). A bundler inlines the call, so your built `index.js` stays
15
+ import-free — exactly what Mutka loads.
13
16
 
14
17
  ## Install
15
18
 
@@ -20,9 +23,9 @@ npm i -D @mutka-explorer/module
20
23
  ## Usage
21
24
 
22
25
  ```ts
23
- import type { SandboxModuleDef } from "@mutka-explorer/module";
26
+ import { defineModule } from "@mutka-explorer/module";
24
27
 
25
- const mod: SandboxModuleDef = {
28
+ export default defineModule({
26
29
  id: "you.hello",
27
30
  name: "Hello",
28
31
  version: "1.0.0",
@@ -31,19 +34,22 @@ const mod: SandboxModuleDef = {
31
34
  { id: "you.hello.count", label: "Count items", contextMenu: true, when: { selection: "any" } },
32
35
  ],
33
36
  setup(host) {
34
- host.onCommand("you.hello.count", async (snap) => {
37
+ host.onCommand("you.hello.count", async (snap) => { // ✓ autocompleted from commands[]
35
38
  const items = await host.fs.readDir(snap.currentDirectory);
36
39
  host.log(`${items.length} items`);
37
40
  });
41
+ // host.onCommand("you.hello.typo", …) ← compile error: not a declared command id
38
42
  },
39
- };
40
-
41
- export default mod;
43
+ });
42
44
  ```
43
45
 
44
46
  `host` is fully typed (`host.fs`, `host.ui`, `host.net`, `host.dialog`, …), as
45
47
  are permissions, `when` clauses, the declarative `UINode` tree, and `FormSchema`.
46
48
 
49
+ > Prefer no runtime import at all? `import type { SandboxModuleDef } from
50
+ > "@mutka-explorer/module"` and annotate `const mod: SandboxModuleDef<"you.hello.count">
51
+ > = { … }` — the generic param enforces the same command-id matching, purely in types.
52
+
47
53
  ### Build to a single file
48
54
 
49
55
  Mutka loads one ESM file with the default export intact. Bundle your TypeScript
package/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- // Type definitions for @mutka-explorer/module v1.0.0-rc.2
1
+ // Type definitions for @mutka-explorer/module v1.0.0-rc.3
2
2
  // Mutka module SDK — author-facing types only (no runtime code).
3
3
  // Generated from the Mutka app source; do not edit by hand.
4
4
 
@@ -99,8 +99,8 @@ export interface WhenClause {
99
99
  * e.g. ["js"]). */
100
100
  extensions?: string[];
101
101
  }
102
- export interface SandboxCommand {
103
- id: string;
102
+ export interface SandboxCommand<Id extends string = string> {
103
+ id: Id;
104
104
  label: string;
105
105
  icon?: string;
106
106
  shortcut?: string;
@@ -456,7 +456,7 @@ export interface ClipboardFiles {
456
456
  }
457
457
  /** Whether a file's data is materialized locally or still cloud-only. */
458
458
  export type CloudStatus = "downloaded" | "cloud";
459
- export interface SandboxHostApi {
459
+ export interface SandboxHostApi<TCommandId extends string = string> {
460
460
  fs: {
461
461
  /** List a directory's entries (works for local paths and provider schemes). */
462
462
  readDir(path: string): Promise<FileItem[]>;
@@ -614,8 +614,10 @@ export interface SandboxHostApi {
614
614
  refresh(): Promise<void>;
615
615
  /** Run an item through the open-resolution pipeline (keyboard double-click). */
616
616
  activate(item: FileItem): Promise<void>;
617
- /** Register the function that runs when one of this module's commands fires. */
618
- onCommand(commandId: string, handler: CommandHandler): void;
617
+ /** Register the function that runs when one of this module's commands fires.
618
+ * When you use `defineModule`, `commandId` is constrained to the ids you
619
+ * declared in `commands[]`, so a typo or stale id is a compile error. */
620
+ onCommand(commandId: TCommandId, handler: CommandHandler): void;
619
621
  /** Register the function that runs when an item matches one of this module's open handlers. */
620
622
  onOpen(handlerId: string, handler: OpenHandler): void;
621
623
  /** Register the value provider for one of this module's custom columns. */
@@ -653,7 +655,7 @@ export interface SandboxHostApi {
653
655
  /** Forwarded to the host console, prefixed with the module id. */
654
656
  log(...args: unknown[]): void;
655
657
  }
656
- export interface SandboxModuleDef {
658
+ export interface SandboxModuleDef<TCommandId extends string = string> {
657
659
  /** Unique module id, "author.name" convention. */
658
660
  id: string;
659
661
  name?: string;
@@ -674,8 +676,10 @@ export interface SandboxModuleDef {
674
676
  tags?: string[];
675
677
  /** Every privileged capability this module uses MUST be listed here. */
676
678
  permissions?: ModulePermission[];
677
- /** Commands surfaced into the app's menus / toolbar. */
678
- commands?: SandboxCommand[];
679
+ /** Commands surfaced into the app's menus / toolbar. The literal `id`s here are
680
+ * captured (via `defineModule`) and become the only values `host.onCommand`
681
+ * accepts in `setup`. */
682
+ commands?: SandboxCommand<TCommandId>[];
679
683
  /** Open handlers (double-click behavior) by item type. */
680
684
  openHandlers?: SandboxOpenHandler[];
681
685
  /** Declarative entries in the left "Places" sidebar, grouped by category. */
@@ -730,8 +734,16 @@ export interface SandboxModuleDef {
730
734
  /**
731
735
  * Runs once after load. Register command/open handlers and event subscriptions
732
736
  * here. Reaches the system only through `host.*` (each gated by permissions).
737
+ * `host.onCommand` is typed to the `commands[].id`s declared above.
733
738
  */
734
- setup?: (host: SandboxHostApi) => void | Promise<void>;
739
+ setup?: (host: SandboxHostApi<TCommandId>) => void | Promise<void>;
735
740
  }
741
+ /**
742
+ * Author-facing helper. At runtime it returns its argument unchanged (so the
743
+ * built file is import-free once bundled); at the type level it infers the union
744
+ * of `commands[].id` literals and threads it into `host.onCommand`, so a typo'd
745
+ * or stale command id is a compile error. With no `commands`, ids stay `string`.
746
+ */
747
+ export declare function defineModule<const TCommandId extends string = string>(def: SandboxModuleDef<TCommandId>): SandboxModuleDef<TCommandId>;
736
748
 
737
749
  export {};
package/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // Runtime for @mutka-explorer/module.
2
+ //
3
+ // The ONLY runtime export is `defineModule`, an identity function — it exists
4
+ // purely so TypeScript can infer your `commands[].id`s and type `host.onCommand`
5
+ // to them (all the typing lives in index.d.ts). A bundler inlines this call, so
6
+ // an authored module's built `index.js` stays import-free and self-contained.
7
+ export const defineModule = (def) => def;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mutka-explorer/module",
3
- "version": "1.0.0-rc.2",
4
- "description": "TypeScript types for authoring Mutka modules — the SandboxModuleDef shape plus the full host API. Types only, no runtime code.",
3
+ "version": "1.0.0-rc.3",
4
+ "description": "Types + the defineModule() helper for authoring Mutka modules — the SandboxModuleDef shape, the full typed host API, and command-id inference.",
5
5
  "license": "MIT",
6
6
  "author": "Mutka contributors",
7
7
  "homepage": "https://github.com/ilianAZZ/mutka",
@@ -17,14 +17,18 @@
17
17
  "sdk",
18
18
  "file-explorer"
19
19
  ],
20
+ "type": "module",
20
21
  "types": "./index.d.ts",
22
+ "main": "./index.js",
21
23
  "exports": {
22
24
  ".": {
23
- "types": "./index.d.ts"
25
+ "types": "./index.d.ts",
26
+ "import": "./index.js"
24
27
  }
25
28
  },
26
29
  "files": [
27
30
  "index.d.ts",
31
+ "index.js",
28
32
  "README.md"
29
33
  ],
30
34
  "publishConfig": {