@microscope-js/core 0.1.2 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,33 +1,77 @@
1
1
  # @microscope-js/core
2
2
 
3
- Framework-agnostic core of microscope-js. Defines the `Renderer` interface and the registry that picks the right renderer for a source.
3
+ [![npm](https://img.shields.io/npm/v/@microscope-js/core?color=cb3837&logo=npm)](https://www.npmjs.com/package/@microscope-js/core)
4
+ [![Bundle](https://img.shields.io/bundlephobia/minzip/@microscope-js/core?label=gzip)](https://bundlephobia.com/package/@microscope-js/core)
5
+ [![Types](https://img.shields.io/npm/types/@microscope-js/core?logo=typescript)](https://www.typescriptlang.org/)
6
+ [![Provenance](https://img.shields.io/badge/SLSA-provenance-blueviolet?logo=npm)](https://docs.npmjs.com/generating-provenance-statements)
7
+
8
+ > **The framework-agnostic heart of [microscope-js](https://github.com/shubham8550/microscope-js).** One `Renderer` interface, one `Registry`, one `mount()`. Everything else is a plugin.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pnpm add @microscope-js/core @microscope-js/renderer-pdf
14
+ ```
15
+
16
+ You almost never use `core` alone — pair it with one or more `@microscope-js/renderer-*` packages.
17
+
18
+ ## Use
4
19
 
5
20
  ```ts
6
21
  import { createRegistry, mount } from '@microscope-js/core';
7
22
  import { pdfRenderer } from '@microscope-js/renderer-pdf';
23
+ import { imageRenderer } from '@microscope-js/renderer-image';
24
+
25
+ const registry = createRegistry([pdfRenderer, imageRenderer]);
8
26
 
9
- const registry = createRegistry([pdfRenderer]);
10
27
  const handle = await mount({
11
- source: file,
28
+ source: fileInput.files[0], // File | Blob | ArrayBuffer | Uint8Array | URL | string
12
29
  container: document.getElementById('viewer')!,
13
30
  registry,
14
31
  });
15
- // later...
32
+
33
+ // later, when navigating away
16
34
  handle.destroy();
17
35
  ```
18
36
 
19
- ## Defining a renderer
37
+ `mount()` normalizes the source, sniffs the MIME type if needed, picks the highest-priority renderer that claims it, clears the container, and renders.
38
+
39
+ ## API
40
+
41
+ ### `createRegistry(renderers)`
42
+
43
+ Build a `Registry` from a list of `Renderer`s. Later entries win by default; pass `{ renderer, priority }` to override.
44
+
45
+ ### `composeRegistries(...registries)`
46
+
47
+ Merge multiple registries — useful when an app layers custom renderers on top of defaults without losing tree-shakability.
48
+
49
+ ### `mount(opts): Promise<RenderHandle>`
50
+
51
+ | Option | Type | Description |
52
+ | ------------- | --------------------------------- | ----------- |
53
+ | `source` | `Source` | What to render — `File`, `Blob`, `ArrayBuffer`, `Uint8Array`, `URL`, or `string` URL |
54
+ | `container` | `HTMLElement` | DOM node to mount into. Cleared before render. |
55
+ | `registry` | `Registry` | Where to look up the renderer |
56
+ | `options?` | `Record<string, unknown>` | Per-render options, forwarded to the matched renderer |
57
+ | `signal?` | `AbortSignal` | Cancels in-flight rendering |
58
+ | `rendererId?` | `string` | Force-pick a renderer by id (skips matching) |
59
+ | `t?` | `(key, fallback) => string` | Translation hook for user-facing strings |
60
+
61
+ Returns a `RenderHandle` with a required `destroy()` and optional renderer-specific `capabilities` (e.g. `pageCount` / `scrollToPage` from PDF, `sheetNames` / `showSheet` from XLSX).
62
+
63
+ ## Define your own renderer
20
64
 
21
65
  ```ts
22
66
  import type { Renderer } from '@microscope-js/core';
23
67
 
24
68
  export const myRenderer: Renderer = {
25
69
  id: 'myformat',
26
- name: 'My Format',
70
+ name: 'My format',
27
71
  mimes: ['application/x-myformat'],
28
72
  extensions: ['myf'],
29
73
  async render({ source, container, signal }) {
30
- // ... mount into container, listen to signal for cancellation
74
+ // …draw `source.blob` into `container`, honor `signal` for cancellation
31
75
  return {
32
76
  destroy() { /* clean up */ },
33
77
  };
@@ -35,4 +79,18 @@ export const myRenderer: Renderer = {
35
79
  };
36
80
  ```
37
81
 
38
- Renderers are pure values — no class instantiation, no global state. The registry handles matching by MIME + extension, with custom `canRender` overrides for byte-sniffing renderers.
82
+ Renderers are plain values — no class instantiation, no global state. The registry handles matching by MIME + extension, with an optional `canRender()` override for byte-sniffing.
83
+
84
+ ## Re-exports
85
+
86
+ For convenience the core barrel also exports `MicroscopeError`, `Source`, `NormalizedSource`, `normalizeSource`, `sniffMime`, and `extOf` from `@microscope-js/utils` — so simple consumers don't need a second install.
87
+
88
+ ## See also
89
+
90
+ - [`@microscope-js/react`](https://www.npmjs.com/package/@microscope-js/react) — `<Viewer />` and `useViewer()` for React / Next.js
91
+ - [`@microscope-js/utils`](https://www.npmjs.com/package/@microscope-js/utils) — shared low-level helpers
92
+ - [Repository](https://github.com/shubham8550/microscope-js) · [Live demo](https://shubham8550.github.io/microscope-js) · [API docs](https://shubham8550.github.io/microscope-js/docs)
93
+
94
+ ## License
95
+
96
+ [MIT](https://github.com/shubham8550/microscope-js/blob/main/LICENSE)
package/dist/index.cjs CHANGED
@@ -2,7 +2,29 @@
2
2
 
3
3
  var utils = require('@microscope-js/utils');
4
4
 
5
- // src/registry.ts
5
+ // src/index.ts
6
+ async function mount(opts) {
7
+ const { source, container, registry, options, signal, t, rendererId } = opts;
8
+ if (!container) {
9
+ throw new utils.MicroscopeError("mount() requires a container element", "INVALID_SOURCE");
10
+ }
11
+ if (signal?.aborted) {
12
+ throw new utils.MicroscopeError("aborted before start", "ABORTED");
13
+ }
14
+ const normalized = await utils.normalizeSource(source);
15
+ if (!normalized.mime) {
16
+ normalized.mime = await utils.sniffMime(normalized.blob);
17
+ }
18
+ const renderer = rendererId ? registry.get(rendererId) : await registry.match(normalized);
19
+ if (!renderer) {
20
+ throw new utils.MicroscopeError(
21
+ `No renderer registered for source (mime=${normalized.mime ?? "unknown"}, name=${normalized.name ?? "unknown"})`,
22
+ "UNSUPPORTED"
23
+ );
24
+ }
25
+ utils.clearContainer(container);
26
+ return renderer.render({ source: normalized, container, options, signal, t });
27
+ }
6
28
  function createRegistry(renderers) {
7
29
  const entries = renderers.map(
8
30
  (r, i) => "renderer" in r ? r : { renderer: r, priority: i }
@@ -49,28 +71,6 @@ function composeRegistries(...regs) {
49
71
  for (const r of regs) merged.push(...r.entries);
50
72
  return createRegistry(merged);
51
73
  }
52
- async function mount(opts) {
53
- const { source, container, registry, options, signal, t, rendererId } = opts;
54
- if (!container) {
55
- throw new utils.MicroscopeError("mount() requires a container element", "INVALID_SOURCE");
56
- }
57
- if (signal?.aborted) {
58
- throw new utils.MicroscopeError("aborted before start", "ABORTED");
59
- }
60
- const normalized = await utils.normalizeSource(source);
61
- if (!normalized.mime) {
62
- normalized.mime = await utils.sniffMime(normalized.blob);
63
- }
64
- const renderer = rendererId ? registry.get(rendererId) : await registry.match(normalized);
65
- if (!renderer) {
66
- throw new utils.MicroscopeError(
67
- `No renderer registered for source (mime=${normalized.mime ?? "unknown"}, name=${normalized.name ?? "unknown"})`,
68
- "UNSUPPORTED"
69
- );
70
- }
71
- utils.clearContainer(container);
72
- return renderer.render({ source: normalized, container, options, signal, t });
73
- }
74
74
 
75
75
  Object.defineProperty(exports, "MicroscopeError", {
76
76
  enumerable: true,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/registry.ts","../src/mount.ts"],"names":["extOf","mimeMatches","MicroscopeError","normalizeSource","sniffMime","clearContainer"],"mappings":"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","file":"index.cjs","sourcesContent":["import { extOf, mimeMatches } from '@microscope-js/utils';\nimport type { Registry, RegistryEntry, Renderer } from './types.js';\n\n/**\n * Build a registry from a list of renderers. Later renderers in the list win\n * unless an explicit priority is given via `{ renderer, priority }`.\n *\n * The registry is intentionally tiny — picking a renderer is just:\n * 1. Run `canRender` overrides first (custom byte-sniffing wins).\n * 2. Otherwise match by MIME, then by extension, then by sniffed MIME.\n * 3. Highest priority wins.\n */\nexport function createRegistry(renderers: ReadonlyArray<Renderer | RegistryEntry>): Registry {\n const entries: RegistryEntry[] = renderers.map((r, i) =>\n 'renderer' in r ? r : { renderer: r, priority: i },\n );\n\n const byId = new Map<string, Renderer>();\n for (const { renderer } of entries) {\n byId.set(renderer.id, renderer);\n }\n\n return {\n entries,\n get(id) {\n return byId.get(id) ?? null;\n },\n async match(source) {\n const ext = extOf(source.name);\n const mime = source.mime;\n\n // First pass — let any renderer with a custom `canRender` claim the source.\n // This is how byte-sniffing renderers (ZIP-based formats) override MIME.\n const candidates: RegistryEntry[] = [];\n for (const entry of entries) {\n const r = entry.renderer;\n if (r.canRender) {\n const ok = await r.canRender(source);\n if (ok) candidates.push(entry);\n continue;\n }\n if (claimsByMeta(r, mime, ext)) candidates.push(entry);\n }\n\n if (candidates.length === 0) return null;\n candidates.sort((a, b) => b.priority - a.priority);\n return candidates[0]?.renderer ?? null;\n },\n };\n}\n\nfunction claimsByMeta(r: Renderer, mime: string | null, ext: string | null): boolean {\n if (mime) {\n for (const m of r.mimes) {\n if (mimeMatches(mime, m)) return true;\n }\n }\n if (ext && r.extensions.includes(ext)) return true;\n return false;\n}\n\n/**\n * Compose two registries — useful when an app wants its own custom renderers\n * layered on top of the default ones without losing tree-shakability.\n */\nexport function composeRegistries(...regs: ReadonlyArray<Registry>): Registry {\n const merged: RegistryEntry[] = [];\n for (const r of regs) merged.push(...r.entries);\n return createRegistry(merged);\n}\n","import { MicroscopeError, clearContainer, normalizeSource, sniffMime } from '@microscope-js/utils';\nimport type { MountOptions, RenderHandle } from './types.js';\n\n/**\n * The single entry point most callers will use. Normalizes the source,\n * sniffs MIME if unknown, picks a renderer from the registry, and renders.\n *\n * Returns a handle whose `destroy()` MUST be called by the caller.\n */\nexport async function mount(opts: MountOptions): Promise<RenderHandle> {\n const { source, container, registry, options, signal, t, rendererId } = opts;\n\n if (!container) {\n throw new MicroscopeError('mount() requires a container element', 'INVALID_SOURCE');\n }\n if (signal?.aborted) {\n throw new MicroscopeError('aborted before start', 'ABORTED');\n }\n\n const normalized = await normalizeSource(source);\n if (!normalized.mime) {\n normalized.mime = await sniffMime(normalized.blob);\n }\n\n const renderer = rendererId ? registry.get(rendererId) : await registry.match(normalized);\n\n if (!renderer) {\n throw new MicroscopeError(\n `No renderer registered for source (mime=${normalized.mime ?? 'unknown'}, name=${normalized.name ?? 'unknown'})`,\n 'UNSUPPORTED',\n );\n }\n\n clearContainer(container);\n return renderer.render({ source: normalized, container, options, signal, t });\n}\n"]}
1
+ {"version":3,"sources":["../src/mount.ts","../src/registry.ts"],"names":["MicroscopeError","normalizeSource","sniffMime","clearContainer","extOf","mimeMatches"],"mappings":";;;;;AASA,eAAsB,MAAM,IAAA,EAA2C;AACrE,EAAA,MAAM,EAAE,QAAQ,SAAA,EAAW,QAAA,EAAU,SAAS,MAAA,EAAQ,CAAA,EAAG,YAAW,GAAI,IAAA;AAExE,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA,MAAM,IAAIA,qBAAA,CAAgB,sCAAA,EAAwC,gBAAgB,CAAA;AAAA,EACpF;AACA,EAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,IAAA,MAAM,IAAIA,qBAAA,CAAgB,sBAAA,EAAwB,SAAS,CAAA;AAAA,EAC7D;AAEA,EAAA,MAAM,UAAA,GAAa,MAAMC,qBAAA,CAAgB,MAAM,CAAA;AAC/C,EAAA,IAAI,CAAC,WAAW,IAAA,EAAM;AACpB,IAAA,UAAA,CAAW,IAAA,GAAO,MAAMC,eAAA,CAAU,UAAA,CAAW,IAAI,CAAA;AAAA,EACnD;AAEA,EAAA,MAAM,QAAA,GAAW,aAAa,QAAA,CAAS,GAAA,CAAI,UAAU,CAAA,GAAI,MAAM,QAAA,CAAS,KAAA,CAAM,UAAU,CAAA;AAExF,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAIF,qBAAA;AAAA,MACR,2CAA2C,UAAA,CAAW,IAAA,IAAQ,SAAS,CAAA,OAAA,EAAU,UAAA,CAAW,QAAQ,SAAS,CAAA,CAAA,CAAA;AAAA,MAC7G;AAAA,KACF;AAAA,EACF;AAEA,EAAAG,oBAAA,CAAe,SAAS,CAAA;AACxB,EAAA,OAAO,QAAA,CAAS,OAAO,EAAE,MAAA,EAAQ,YAAY,SAAA,EAAW,OAAA,EAAS,MAAA,EAAQ,CAAA,EAAG,CAAA;AAC9E;ACvBO,SAAS,eAAe,SAAA,EAA8D;AAC3F,EAAA,MAAM,UAA2B,SAAA,CAAU,GAAA;AAAA,IAAI,CAAC,CAAA,EAAG,CAAA,KACjD,UAAA,IAAc,CAAA,GAAI,IAAI,EAAE,QAAA,EAAU,CAAA,EAAG,QAAA,EAAU,CAAA;AAAE,GACnD;AAEA,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAsB;AACvC,EAAA,KAAA,MAAW,EAAE,QAAA,EAAS,IAAK,OAAA,EAAS;AAClC,IAAA,IAAA,CAAK,GAAA,CAAI,QAAA,CAAS,EAAA,EAAI,QAAQ,CAAA;AAAA,EAChC;AAEA,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,IAAI,EAAA,EAAI;AACN,MAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,CAAA,IAAK,IAAA;AAAA,IACzB,CAAA;AAAA,IACA,MAAM,MAAM,MAAA,EAAQ;AAClB,MAAA,MAAM,GAAA,GAAMC,WAAA,CAAM,MAAA,CAAO,IAAI,CAAA;AAC7B,MAAA,MAAM,OAAO,MAAA,CAAO,IAAA;AAIpB,MAAA,MAAM,aAA8B,EAAC;AACrC,MAAA,KAAA,MAAW,SAAS,OAAA,EAAS;AAC3B,QAAA,MAAM,IAAI,KAAA,CAAM,QAAA;AAChB,QAAA,IAAI,EAAE,SAAA,EAAW;AACf,UAAA,MAAM,EAAA,GAAK,MAAM,CAAA,CAAE,SAAA,CAAU,MAAM,CAAA;AACnC,UAAA,IAAI,EAAA,EAAI,UAAA,CAAW,IAAA,CAAK,KAAK,CAAA;AAC7B,UAAA;AAAA,QACF;AACA,QAAA,IAAI,aAAa,CAAA,EAAG,IAAA,EAAM,GAAG,CAAA,EAAG,UAAA,CAAW,KAAK,KAAK,CAAA;AAAA,MACvD;AAEA,MAAA,IAAI,UAAA,CAAW,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AACpC,MAAA,UAAA,CAAW,KAAK,CAAC,CAAA,EAAG,MAAM,CAAA,CAAE,QAAA,GAAW,EAAE,QAAQ,CAAA;AACjD,MAAA,OAAO,UAAA,CAAW,CAAC,CAAA,EAAG,QAAA,IAAY,IAAA;AAAA,IACpC;AAAA,GACF;AACF;AAEA,SAAS,YAAA,CAAa,CAAA,EAAa,IAAA,EAAqB,GAAA,EAA6B;AACnF,EAAA,IAAI,IAAA,EAAM;AACR,IAAA,KAAA,MAAW,CAAA,IAAK,EAAE,KAAA,EAAO;AACvB,MAAA,IAAIC,iBAAA,CAAY,IAAA,EAAM,CAAC,CAAA,EAAG,OAAO,IAAA;AAAA,IACnC;AAAA,EACF;AACA,EAAA,IAAI,OAAO,CAAA,CAAE,UAAA,CAAW,QAAA,CAAS,GAAG,GAAG,OAAO,IAAA;AAC9C,EAAA,OAAO,KAAA;AACT;AAMO,SAAS,qBAAqB,IAAA,EAAyC;AAC5E,EAAA,MAAM,SAA0B,EAAC;AACjC,EAAA,KAAA,MAAW,KAAK,IAAA,EAAM,MAAA,CAAO,IAAA,CAAK,GAAG,EAAE,OAAO,CAAA;AAC9C,EAAA,OAAO,eAAe,MAAM,CAAA;AAC9B","file":"index.cjs","sourcesContent":["import { clearContainer, MicroscopeError, normalizeSource, sniffMime } from '@microscope-js/utils';\nimport type { MountOptions, RenderHandle } from './types.js';\n\n/**\n * The single entry point most callers will use. Normalizes the source,\n * sniffs MIME if unknown, picks a renderer from the registry, and renders.\n *\n * Returns a handle whose `destroy()` MUST be called by the caller.\n */\nexport async function mount(opts: MountOptions): Promise<RenderHandle> {\n const { source, container, registry, options, signal, t, rendererId } = opts;\n\n if (!container) {\n throw new MicroscopeError('mount() requires a container element', 'INVALID_SOURCE');\n }\n if (signal?.aborted) {\n throw new MicroscopeError('aborted before start', 'ABORTED');\n }\n\n const normalized = await normalizeSource(source);\n if (!normalized.mime) {\n normalized.mime = await sniffMime(normalized.blob);\n }\n\n const renderer = rendererId ? registry.get(rendererId) : await registry.match(normalized);\n\n if (!renderer) {\n throw new MicroscopeError(\n `No renderer registered for source (mime=${normalized.mime ?? 'unknown'}, name=${normalized.name ?? 'unknown'})`,\n 'UNSUPPORTED',\n );\n }\n\n clearContainer(container);\n return renderer.render({ source: normalized, container, options, signal, t });\n}\n","import { extOf, mimeMatches } from '@microscope-js/utils';\nimport type { Registry, RegistryEntry, Renderer } from './types.js';\n\n/**\n * Build a registry from a list of renderers. Later renderers in the list win\n * unless an explicit priority is given via `{ renderer, priority }`.\n *\n * The registry is intentionally tiny — picking a renderer is just:\n * 1. Run `canRender` overrides first (custom byte-sniffing wins).\n * 2. Otherwise match by MIME, then by extension, then by sniffed MIME.\n * 3. Highest priority wins.\n */\nexport function createRegistry(renderers: ReadonlyArray<Renderer | RegistryEntry>): Registry {\n const entries: RegistryEntry[] = renderers.map((r, i) =>\n 'renderer' in r ? r : { renderer: r, priority: i },\n );\n\n const byId = new Map<string, Renderer>();\n for (const { renderer } of entries) {\n byId.set(renderer.id, renderer);\n }\n\n return {\n entries,\n get(id) {\n return byId.get(id) ?? null;\n },\n async match(source) {\n const ext = extOf(source.name);\n const mime = source.mime;\n\n // First pass — let any renderer with a custom `canRender` claim the source.\n // This is how byte-sniffing renderers (ZIP-based formats) override MIME.\n const candidates: RegistryEntry[] = [];\n for (const entry of entries) {\n const r = entry.renderer;\n if (r.canRender) {\n const ok = await r.canRender(source);\n if (ok) candidates.push(entry);\n continue;\n }\n if (claimsByMeta(r, mime, ext)) candidates.push(entry);\n }\n\n if (candidates.length === 0) return null;\n candidates.sort((a, b) => b.priority - a.priority);\n return candidates[0]?.renderer ?? null;\n },\n };\n}\n\nfunction claimsByMeta(r: Renderer, mime: string | null, ext: string | null): boolean {\n if (mime) {\n for (const m of r.mimes) {\n if (mimeMatches(mime, m)) return true;\n }\n }\n if (ext && r.extensions.includes(ext)) return true;\n return false;\n}\n\n/**\n * Compose two registries — useful when an app wants its own custom renderers\n * layered on top of the default ones without losing tree-shakability.\n */\nexport function composeRegistries(...regs: ReadonlyArray<Registry>): Registry {\n const merged: RegistryEntry[] = [];\n for (const r of regs) merged.push(...r.entries);\n return createRegistry(merged);\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -69,6 +69,14 @@ interface Registry {
69
69
  get(id: string): Renderer | null;
70
70
  }
71
71
 
72
+ /**
73
+ * The single entry point most callers will use. Normalizes the source,
74
+ * sniffs MIME if unknown, picks a renderer from the registry, and renders.
75
+ *
76
+ * Returns a handle whose `destroy()` MUST be called by the caller.
77
+ */
78
+ declare function mount(opts: MountOptions): Promise<RenderHandle>;
79
+
72
80
  /**
73
81
  * Build a registry from a list of renderers. Later renderers in the list win
74
82
  * unless an explicit priority is given via `{ renderer, priority }`.
@@ -85,12 +93,4 @@ declare function createRegistry(renderers: ReadonlyArray<Renderer | RegistryEntr
85
93
  */
86
94
  declare function composeRegistries(...regs: ReadonlyArray<Registry>): Registry;
87
95
 
88
- /**
89
- * The single entry point most callers will use. Normalizes the source,
90
- * sniffs MIME if unknown, picks a renderer from the registry, and renders.
91
- *
92
- * Returns a handle whose `destroy()` MUST be called by the caller.
93
- */
94
- declare function mount(opts: MountOptions): Promise<RenderHandle>;
95
-
96
96
  export { type MountOptions, type Registry, type RegistryEntry, type RenderContext, type RenderHandle, type Renderer, type RendererMeta, composeRegistries, createRegistry, mount };
package/dist/index.d.ts CHANGED
@@ -69,6 +69,14 @@ interface Registry {
69
69
  get(id: string): Renderer | null;
70
70
  }
71
71
 
72
+ /**
73
+ * The single entry point most callers will use. Normalizes the source,
74
+ * sniffs MIME if unknown, picks a renderer from the registry, and renders.
75
+ *
76
+ * Returns a handle whose `destroy()` MUST be called by the caller.
77
+ */
78
+ declare function mount(opts: MountOptions): Promise<RenderHandle>;
79
+
72
80
  /**
73
81
  * Build a registry from a list of renderers. Later renderers in the list win
74
82
  * unless an explicit priority is given via `{ renderer, priority }`.
@@ -85,12 +93,4 @@ declare function createRegistry(renderers: ReadonlyArray<Renderer | RegistryEntr
85
93
  */
86
94
  declare function composeRegistries(...regs: ReadonlyArray<Registry>): Registry;
87
95
 
88
- /**
89
- * The single entry point most callers will use. Normalizes the source,
90
- * sniffs MIME if unknown, picks a renderer from the registry, and renders.
91
- *
92
- * Returns a handle whose `destroy()` MUST be called by the caller.
93
- */
94
- declare function mount(opts: MountOptions): Promise<RenderHandle>;
95
-
96
96
  export { type MountOptions, type Registry, type RegistryEntry, type RenderContext, type RenderHandle, type Renderer, type RendererMeta, composeRegistries, createRegistry, mount };
package/dist/index.js CHANGED
@@ -1,7 +1,29 @@
1
- import { extOf, mimeMatches, MicroscopeError, normalizeSource, sniffMime, clearContainer } from '@microscope-js/utils';
1
+ import { MicroscopeError, normalizeSource, sniffMime, clearContainer, extOf, mimeMatches } from '@microscope-js/utils';
2
2
  export { MicroscopeError, extOf, normalizeSource, sniffMime } from '@microscope-js/utils';
3
3
 
4
- // src/registry.ts
4
+ // src/index.ts
5
+ async function mount(opts) {
6
+ const { source, container, registry, options, signal, t, rendererId } = opts;
7
+ if (!container) {
8
+ throw new MicroscopeError("mount() requires a container element", "INVALID_SOURCE");
9
+ }
10
+ if (signal?.aborted) {
11
+ throw new MicroscopeError("aborted before start", "ABORTED");
12
+ }
13
+ const normalized = await normalizeSource(source);
14
+ if (!normalized.mime) {
15
+ normalized.mime = await sniffMime(normalized.blob);
16
+ }
17
+ const renderer = rendererId ? registry.get(rendererId) : await registry.match(normalized);
18
+ if (!renderer) {
19
+ throw new MicroscopeError(
20
+ `No renderer registered for source (mime=${normalized.mime ?? "unknown"}, name=${normalized.name ?? "unknown"})`,
21
+ "UNSUPPORTED"
22
+ );
23
+ }
24
+ clearContainer(container);
25
+ return renderer.render({ source: normalized, container, options, signal, t });
26
+ }
5
27
  function createRegistry(renderers) {
6
28
  const entries = renderers.map(
7
29
  (r, i) => "renderer" in r ? r : { renderer: r, priority: i }
@@ -48,28 +70,6 @@ function composeRegistries(...regs) {
48
70
  for (const r of regs) merged.push(...r.entries);
49
71
  return createRegistry(merged);
50
72
  }
51
- async function mount(opts) {
52
- const { source, container, registry, options, signal, t, rendererId } = opts;
53
- if (!container) {
54
- throw new MicroscopeError("mount() requires a container element", "INVALID_SOURCE");
55
- }
56
- if (signal?.aborted) {
57
- throw new MicroscopeError("aborted before start", "ABORTED");
58
- }
59
- const normalized = await normalizeSource(source);
60
- if (!normalized.mime) {
61
- normalized.mime = await sniffMime(normalized.blob);
62
- }
63
- const renderer = rendererId ? registry.get(rendererId) : await registry.match(normalized);
64
- if (!renderer) {
65
- throw new MicroscopeError(
66
- `No renderer registered for source (mime=${normalized.mime ?? "unknown"}, name=${normalized.name ?? "unknown"})`,
67
- "UNSUPPORTED"
68
- );
69
- }
70
- clearContainer(container);
71
- return renderer.render({ source: normalized, container, options, signal, t });
72
- }
73
73
 
74
74
  export { composeRegistries, createRegistry, mount };
75
75
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/registry.ts","../src/mount.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { extOf, mimeMatches } from '@microscope-js/utils';\nimport type { Registry, RegistryEntry, Renderer } from './types.js';\n\n/**\n * Build a registry from a list of renderers. Later renderers in the list win\n * unless an explicit priority is given via `{ renderer, priority }`.\n *\n * The registry is intentionally tiny — picking a renderer is just:\n * 1. Run `canRender` overrides first (custom byte-sniffing wins).\n * 2. Otherwise match by MIME, then by extension, then by sniffed MIME.\n * 3. Highest priority wins.\n */\nexport function createRegistry(renderers: ReadonlyArray<Renderer | RegistryEntry>): Registry {\n const entries: RegistryEntry[] = renderers.map((r, i) =>\n 'renderer' in r ? r : { renderer: r, priority: i },\n );\n\n const byId = new Map<string, Renderer>();\n for (const { renderer } of entries) {\n byId.set(renderer.id, renderer);\n }\n\n return {\n entries,\n get(id) {\n return byId.get(id) ?? null;\n },\n async match(source) {\n const ext = extOf(source.name);\n const mime = source.mime;\n\n // First pass — let any renderer with a custom `canRender` claim the source.\n // This is how byte-sniffing renderers (ZIP-based formats) override MIME.\n const candidates: RegistryEntry[] = [];\n for (const entry of entries) {\n const r = entry.renderer;\n if (r.canRender) {\n const ok = await r.canRender(source);\n if (ok) candidates.push(entry);\n continue;\n }\n if (claimsByMeta(r, mime, ext)) candidates.push(entry);\n }\n\n if (candidates.length === 0) return null;\n candidates.sort((a, b) => b.priority - a.priority);\n return candidates[0]?.renderer ?? null;\n },\n };\n}\n\nfunction claimsByMeta(r: Renderer, mime: string | null, ext: string | null): boolean {\n if (mime) {\n for (const m of r.mimes) {\n if (mimeMatches(mime, m)) return true;\n }\n }\n if (ext && r.extensions.includes(ext)) return true;\n return false;\n}\n\n/**\n * Compose two registries — useful when an app wants its own custom renderers\n * layered on top of the default ones without losing tree-shakability.\n */\nexport function composeRegistries(...regs: ReadonlyArray<Registry>): Registry {\n const merged: RegistryEntry[] = [];\n for (const r of regs) merged.push(...r.entries);\n return createRegistry(merged);\n}\n","import { MicroscopeError, clearContainer, normalizeSource, sniffMime } from '@microscope-js/utils';\nimport type { MountOptions, RenderHandle } from './types.js';\n\n/**\n * The single entry point most callers will use. Normalizes the source,\n * sniffs MIME if unknown, picks a renderer from the registry, and renders.\n *\n * Returns a handle whose `destroy()` MUST be called by the caller.\n */\nexport async function mount(opts: MountOptions): Promise<RenderHandle> {\n const { source, container, registry, options, signal, t, rendererId } = opts;\n\n if (!container) {\n throw new MicroscopeError('mount() requires a container element', 'INVALID_SOURCE');\n }\n if (signal?.aborted) {\n throw new MicroscopeError('aborted before start', 'ABORTED');\n }\n\n const normalized = await normalizeSource(source);\n if (!normalized.mime) {\n normalized.mime = await sniffMime(normalized.blob);\n }\n\n const renderer = rendererId ? registry.get(rendererId) : await registry.match(normalized);\n\n if (!renderer) {\n throw new MicroscopeError(\n `No renderer registered for source (mime=${normalized.mime ?? 'unknown'}, name=${normalized.name ?? 'unknown'})`,\n 'UNSUPPORTED',\n );\n }\n\n clearContainer(container);\n return renderer.render({ source: normalized, container, options, signal, t });\n}\n"]}
1
+ {"version":3,"sources":["../src/mount.ts","../src/registry.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { clearContainer, MicroscopeError, normalizeSource, sniffMime } from '@microscope-js/utils';\nimport type { MountOptions, RenderHandle } from './types.js';\n\n/**\n * The single entry point most callers will use. Normalizes the source,\n * sniffs MIME if unknown, picks a renderer from the registry, and renders.\n *\n * Returns a handle whose `destroy()` MUST be called by the caller.\n */\nexport async function mount(opts: MountOptions): Promise<RenderHandle> {\n const { source, container, registry, options, signal, t, rendererId } = opts;\n\n if (!container) {\n throw new MicroscopeError('mount() requires a container element', 'INVALID_SOURCE');\n }\n if (signal?.aborted) {\n throw new MicroscopeError('aborted before start', 'ABORTED');\n }\n\n const normalized = await normalizeSource(source);\n if (!normalized.mime) {\n normalized.mime = await sniffMime(normalized.blob);\n }\n\n const renderer = rendererId ? registry.get(rendererId) : await registry.match(normalized);\n\n if (!renderer) {\n throw new MicroscopeError(\n `No renderer registered for source (mime=${normalized.mime ?? 'unknown'}, name=${normalized.name ?? 'unknown'})`,\n 'UNSUPPORTED',\n );\n }\n\n clearContainer(container);\n return renderer.render({ source: normalized, container, options, signal, t });\n}\n","import { extOf, mimeMatches } from '@microscope-js/utils';\nimport type { Registry, RegistryEntry, Renderer } from './types.js';\n\n/**\n * Build a registry from a list of renderers. Later renderers in the list win\n * unless an explicit priority is given via `{ renderer, priority }`.\n *\n * The registry is intentionally tiny — picking a renderer is just:\n * 1. Run `canRender` overrides first (custom byte-sniffing wins).\n * 2. Otherwise match by MIME, then by extension, then by sniffed MIME.\n * 3. Highest priority wins.\n */\nexport function createRegistry(renderers: ReadonlyArray<Renderer | RegistryEntry>): Registry {\n const entries: RegistryEntry[] = renderers.map((r, i) =>\n 'renderer' in r ? r : { renderer: r, priority: i },\n );\n\n const byId = new Map<string, Renderer>();\n for (const { renderer } of entries) {\n byId.set(renderer.id, renderer);\n }\n\n return {\n entries,\n get(id) {\n return byId.get(id) ?? null;\n },\n async match(source) {\n const ext = extOf(source.name);\n const mime = source.mime;\n\n // First pass — let any renderer with a custom `canRender` claim the source.\n // This is how byte-sniffing renderers (ZIP-based formats) override MIME.\n const candidates: RegistryEntry[] = [];\n for (const entry of entries) {\n const r = entry.renderer;\n if (r.canRender) {\n const ok = await r.canRender(source);\n if (ok) candidates.push(entry);\n continue;\n }\n if (claimsByMeta(r, mime, ext)) candidates.push(entry);\n }\n\n if (candidates.length === 0) return null;\n candidates.sort((a, b) => b.priority - a.priority);\n return candidates[0]?.renderer ?? null;\n },\n };\n}\n\nfunction claimsByMeta(r: Renderer, mime: string | null, ext: string | null): boolean {\n if (mime) {\n for (const m of r.mimes) {\n if (mimeMatches(mime, m)) return true;\n }\n }\n if (ext && r.extensions.includes(ext)) return true;\n return false;\n}\n\n/**\n * Compose two registries — useful when an app wants its own custom renderers\n * layered on top of the default ones without losing tree-shakability.\n */\nexport function composeRegistries(...regs: ReadonlyArray<Registry>): Registry {\n const merged: RegistryEntry[] = [];\n for (const r of regs) merged.push(...r.entries);\n return createRegistry(merged);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microscope-js/core",
3
- "version": "0.1.2",
3
+ "version": "0.1.5",
4
4
  "description": "Framework-agnostic core for microscope-js: Renderer interface + registry + mount() entry point",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -20,7 +20,7 @@
20
20
  "README.md"
21
21
  ],
22
22
  "dependencies": {
23
- "@microscope-js/utils": "0.1.2"
23
+ "@microscope-js/utils": "0.1.5"
24
24
  },
25
25
  "devDependencies": {
26
26
  "tsup": "^8.3.5",