@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 +66 -8
- package/dist/index.cjs +23 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -8
- package/dist/index.d.ts +8 -8
- package/dist/index.js +24 -24
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,33 +1,77 @@
|
|
|
1
1
|
# @microscope-js/core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@microscope-js/core)
|
|
4
|
+
[](https://bundlephobia.com/package/@microscope-js/core)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
[](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:
|
|
28
|
+
source: fileInput.files[0], // File | Blob | ArrayBuffer | Uint8Array | URL | string
|
|
12
29
|
container: document.getElementById('viewer')!,
|
|
13
30
|
registry,
|
|
14
31
|
});
|
|
15
|
-
|
|
32
|
+
|
|
33
|
+
// later, when navigating away
|
|
16
34
|
handle.destroy();
|
|
17
35
|
```
|
|
18
36
|
|
|
19
|
-
|
|
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
|
|
70
|
+
name: 'My format',
|
|
27
71
|
mimes: ['application/x-myformat'],
|
|
28
72
|
extensions: ['myf'],
|
|
29
73
|
async render({ source, container, signal }) {
|
|
30
|
-
//
|
|
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
|
|
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/
|
|
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,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/
|
|
1
|
+
{"version":3,"sources":["../src/mount.ts","../src/registry.ts"],"names":["MicroscopeError","normalizeSource","sniffMime","clearContainer","extOf","mimeMatches"],"mappings":"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","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 {
|
|
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/
|
|
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/
|
|
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.
|
|
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.
|
|
23
|
+
"@microscope-js/utils": "0.1.5"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"tsup": "^8.3.5",
|