@silurus/ooxml 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yuki Yokotani
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,499 @@
1
+ > **This entire codebase — Rust parsers, TypeScript renderers, tests, and tooling — was implemented by [Claude](https://claude.ai)** (Anthropic's AI assistant) through iterative prompting. No human-written application code exists in this repository.
2
+
3
+ # office-open-xml-viewer
4
+
5
+ **[Demo (Storybook)](https://yukiyokotani.github.io/office-open-xml-viewer/)**
6
+
7
+ A browser-based viewer for Office Open XML documents that renders to an HTML Canvas element.
8
+ The parsers are written in Rust and compiled to WebAssembly; the renderers use the Canvas 2D API.
9
+
10
+ ```bash
11
+ npm install @silurus/ooxml
12
+ # or
13
+ pnpm add @silurus/ooxml
14
+ ```
15
+
16
+ > **Bundler note**: this package embeds `.wasm` files. With Vite add [`vite-plugin-wasm`](https://github.com/Menci/vite-plugin-wasm); with webpack use [`experiments.asyncWebAssembly`](https://webpack.js.org/configuration/experiments/).
17
+
18
+ ---
19
+
20
+ ## Quick Start
21
+
22
+ ```typescript
23
+ import { PptxViewer } from '@silurus/ooxml/pptx';
24
+ import { XlsxViewer } from '@silurus/ooxml/xlsx';
25
+ import { DocxViewer } from '@silurus/ooxml/docx';
26
+
27
+ // PPTX — viewer manages its own <canvas>
28
+ const pptx = new PptxViewer(document.getElementById('pptx-container')!);
29
+ const buf = await fetch('/deck.pptx').then(r => r.arrayBuffer());
30
+ await pptx.load(buf);
31
+ pptx.nextSlide();
32
+
33
+ // XLSX — viewer manages its own <canvas> + tab bar
34
+ const xlsx = new XlsxViewer(document.getElementById('xlsx-container')!);
35
+ await xlsx.load('/workbook.xlsx');
36
+
37
+ // DOCX — caller provides the <canvas>
38
+ const canvas = document.getElementById('docx-canvas') as HTMLCanvasElement;
39
+ const docx = new DocxViewer(canvas);
40
+ await docx.load('/document.docx');
41
+ docx.nextPage();
42
+ ```
43
+
44
+ ---
45
+
46
+ <details>
47
+ <summary><strong>Architecture diagram</strong></summary>
48
+
49
+ ```mermaid
50
+ flowchart TB
51
+ subgraph build["🦀 Build-time (Rust → WebAssembly)"]
52
+ direction LR
53
+ pptx_rs["packages/pptx/parser/src/lib.rs"]
54
+ xlsx_rs["packages/xlsx/parser/src/lib.rs"]
55
+ docx_rs["packages/docx/parser/src/lib.rs"]
56
+ pptx_rs -- wasm-pack --> pptx_wasm["pptx_parser.wasm"]
57
+ xlsx_rs -- wasm-pack --> xlsx_wasm["xlsx_parser.wasm"]
58
+ docx_rs -- wasm-pack --> docx_wasm["docx_parser.wasm"]
59
+ end
60
+
61
+ subgraph browser["🌐 Runtime (Browser)"]
62
+ subgraph pptx_pkg["@silurus/ooxml · pptx"]
63
+ PV["PptxViewer"] --> PP["PptxPresentation"]
64
+ PP --> PW["worker.ts\n〈Web Worker — parse only〉"]
65
+ PP --> PR["renderer.ts\n〈Canvas 2D — main thread〉"]
66
+ end
67
+ subgraph xlsx_pkg["@silurus/ooxml · xlsx"]
68
+ XV["XlsxViewer"] --> XR["renderer.ts\n〈Canvas 2D〉"]
69
+ end
70
+ subgraph docx_pkg["@silurus/ooxml · docx"]
71
+ DV["DocxViewer"] --> DD["DocxDocument"]
72
+ DD --> DR["renderer.ts\n〈Canvas 2D〉"]
73
+ end
74
+ end
75
+
76
+ pptx_wasm --> PW
77
+ xlsx_wasm --> XV
78
+ docx_wasm --> DD
79
+ PR --> canvas["&lt;canvas&gt;"]
80
+ XR --> canvas
81
+ DR --> canvas
82
+ ```
83
+
84
+ The pptx worker parses the `.pptx` archive via WASM and returns a JSON model to the main thread. Rendering runs on the main thread so the canvas shares the document's `FontFaceSet` — an `OffscreenCanvas` in a worker has its own font registry and would silently fall back to a system font, producing subtly different text measurements (and wrap positions) from the installed theme webfonts.
85
+
86
+ ### Key files
87
+
88
+ | File | Role |
89
+ |------|------|
90
+ | `packages/pptx/parser/src/lib.rs` | Rust WASM parser — PPTX ZIP → `Presentation` JSON |
91
+ | `packages/xlsx/parser/src/lib.rs` | Rust WASM parser — XLSX ZIP → `Workbook` JSON |
92
+ | `packages/docx/parser/src/lib.rs` | Rust WASM parser — DOCX ZIP → `Document` JSON |
93
+ | `packages/pptx/src/renderer.ts` | Canvas 2D rendering engine (runs on main thread) |
94
+ | `packages/xlsx/src/renderer.ts` | Canvas 2D rendering engine with virtual scroll |
95
+ | `packages/docx/src/renderer.ts` | Canvas 2D rendering engine with text layout |
96
+ | `packages/pptx/src/worker.ts` | Web Worker: WASM init and parsing only |
97
+ | `packages/*/src/viewer.ts` | Public Viewer API — canvas lifecycle, navigation |
98
+
99
+ </details>
100
+
101
+ ---
102
+
103
+ ## Framework Examples
104
+
105
+ <details>
106
+ <summary><strong>React 19</strong></summary>
107
+
108
+ ```tsx
109
+ // React 19.1 — vite-plugin-wasm required in vite.config.ts
110
+ import { useEffect, useRef, useState } from 'react';
111
+ import { PptxViewer } from '@silurus/ooxml/pptx';
112
+
113
+ export function PptxViewerComponent({ src }: { src: string }) {
114
+ const containerRef = useRef<HTMLDivElement>(null);
115
+ const viewerRef = useRef<PptxViewer | null>(null);
116
+ const [slide, setSlide] = useState({ current: 0, total: 0 });
117
+
118
+ useEffect(() => {
119
+ const container = containerRef.current;
120
+ if (!container) return;
121
+
122
+ const viewer = new PptxViewer(container, {
123
+ onSlideChange: (i, total) => setSlide({ current: i, total }),
124
+ });
125
+ viewerRef.current = viewer;
126
+
127
+ let cancelled = false;
128
+ fetch(src)
129
+ .then(r => r.arrayBuffer())
130
+ .then(buf => { if (!cancelled) viewer.load(buf); });
131
+
132
+ return () => { cancelled = true; };
133
+ }, [src]);
134
+
135
+ return (
136
+ <div>
137
+ <div ref={containerRef} style={{ width: 800 }} />
138
+ <button onClick={() => viewerRef.current?.prevSlide()}>‹ Prev</button>
139
+ <span> {slide.current + 1} / {slide.total} </span>
140
+ <button onClick={() => viewerRef.current?.nextSlide()}>Next ›</button>
141
+ </div>
142
+ );
143
+ }
144
+ ```
145
+
146
+ </details>
147
+
148
+ <details>
149
+ <summary><strong>Vue 3.5</strong></summary>
150
+
151
+ ```vue
152
+ <!-- Vue 3.5 — useTemplateRef is a 3.5+ feature -->
153
+ <script setup lang="ts">
154
+ import { useTemplateRef, onMounted, ref } from 'vue';
155
+ import { PptxViewer } from '@silurus/ooxml/pptx';
156
+
157
+ const props = defineProps<{ src: string }>();
158
+
159
+ const container = useTemplateRef<HTMLDivElement>('container');
160
+ let viewer: PptxViewer | null = null;
161
+ const current = ref(0);
162
+ const total = ref(0);
163
+
164
+ onMounted(async () => {
165
+ viewer = new PptxViewer(container.value!, {
166
+ onSlideChange: (i, t) => { current.value = i; total.value = t; },
167
+ });
168
+ const buf = await fetch(props.src).then(r => r.arrayBuffer());
169
+ await viewer.load(buf);
170
+ });
171
+ </script>
172
+
173
+ <template>
174
+ <div>
175
+ <div ref="container" style="width: 800px" />
176
+ <button @click="viewer?.prevSlide()">‹ Prev</button>
177
+ <span> {{ current + 1 }} / {{ total }} </span>
178
+ <button @click="viewer?.nextSlide()">Next ›</button>
179
+ </div>
180
+ </template>
181
+ ```
182
+
183
+ </details>
184
+
185
+ <details>
186
+ <summary><strong>Angular 19</strong></summary>
187
+
188
+ ```typescript
189
+ // Angular 19 — standalone component with signal-based state
190
+ import {
191
+ Component, ElementRef, viewChild,
192
+ signal, AfterViewInit,
193
+ } from '@angular/core';
194
+ import { PptxViewer } from '@silurus/ooxml/pptx';
195
+
196
+ @Component({
197
+ selector: 'app-pptx-viewer',
198
+ standalone: true,
199
+ template: `
200
+ <div>
201
+ <div #container style="width: 800px"></div>
202
+ <button (click)="prev()">‹ Prev</button>
203
+ <span> {{ current() + 1 }} / {{ total() }} </span>
204
+ <button (click)="next()">Next ›</button>
205
+ </div>
206
+ `,
207
+ })
208
+ export class PptxViewerComponent implements AfterViewInit {
209
+ containerEl = viewChild.required<ElementRef<HTMLDivElement>>('container');
210
+ current = signal(0);
211
+ total = signal(0);
212
+ private viewer?: PptxViewer;
213
+
214
+ ngAfterViewInit(): void {
215
+ this.viewer = new PptxViewer(this.containerEl().nativeElement, {
216
+ onSlideChange: (i, t) => { this.current.set(i); this.total.set(t); },
217
+ });
218
+ fetch('/deck.pptx')
219
+ .then(r => r.arrayBuffer())
220
+ .then(buf => this.viewer!.load(buf));
221
+ }
222
+
223
+ prev(): void { this.viewer?.prevSlide(); }
224
+ next(): void { this.viewer?.nextSlide(); }
225
+ }
226
+ ```
227
+
228
+ > Add `"allowSyntheticDefaultImports": true` and configure `@angular-builders/custom-webpack` (or use `esbuild` builder) with WASM support in your Angular workspace.
229
+
230
+ </details>
231
+
232
+ <details>
233
+ <summary><strong>Svelte 5</strong></summary>
234
+
235
+ ```svelte
236
+ <!-- Svelte 5 — runes syntax ($props, $state) -->
237
+ <script lang="ts">
238
+ import { onMount } from 'svelte';
239
+ import { PptxViewer } from '@silurus/ooxml/pptx';
240
+
241
+ let { src }: { src: string } = $props();
242
+
243
+ let container: HTMLDivElement;
244
+ let viewer: PptxViewer;
245
+ let current = $state(0);
246
+ let total = $state(0);
247
+
248
+ onMount(async () => {
249
+ viewer = new PptxViewer(container, {
250
+ onSlideChange: (i, t) => { current = i; total = t; },
251
+ });
252
+ const buf = await fetch(src).then(r => r.arrayBuffer());
253
+ await viewer.load(buf);
254
+ });
255
+ </script>
256
+
257
+ <div>
258
+ <div bind:this={container} style="width: 800px"></div>
259
+ <button onclick={() => viewer?.prevSlide()}>‹ Prev</button>
260
+ <span> {current + 1} / {total} </span>
261
+ <button onclick={() => viewer?.nextSlide()}>Next ›</button>
262
+ </div>
263
+ ```
264
+
265
+ </details>
266
+
267
+ <details>
268
+ <summary><strong>SolidJS 1.9</strong></summary>
269
+
270
+ ```tsx
271
+ // SolidJS 1.9
272
+ import { createSignal, onMount, onCleanup } from 'solid-js';
273
+ import { PptxViewer } from '@silurus/ooxml/pptx';
274
+
275
+ export function PptxViewerComponent(props: { src: string }) {
276
+ let containerEl!: HTMLDivElement;
277
+ let viewer: PptxViewer | undefined;
278
+ const [current, setCurrent] = createSignal(0);
279
+ const [total, setTotal ] = createSignal(0);
280
+
281
+ onMount(async () => {
282
+ viewer = new PptxViewer(containerEl, {
283
+ onSlideChange: (i, t) => { setCurrent(i); setTotal(t); },
284
+ });
285
+ const buf = await fetch(props.src).then(r => r.arrayBuffer());
286
+ await viewer.load(buf);
287
+ });
288
+
289
+ onCleanup(() => { /* viewer?.destroy?.() */ });
290
+
291
+ return (
292
+ <div>
293
+ <div ref={containerEl} style={{ width: '800px' }} />
294
+ <button onClick={() => viewer?.prevSlide()}>‹ Prev</button>
295
+ <span> {current() + 1} / {total()} </span>
296
+ <button onClick={() => viewer?.nextSlide()}>Next ›</button>
297
+ </div>
298
+ );
299
+ }
300
+ ```
301
+
302
+ </details>
303
+
304
+ <details>
305
+ <summary><strong>Qwik 2</strong></summary>
306
+
307
+ ```tsx
308
+ // Qwik 2.0 — dynamic import to keep WASM out of SSR bundle
309
+ import { component$, useSignal, useVisibleTask$ } from '@builder.io/qwik';
310
+ import type { PptxViewer as PptxViewerType } from '@silurus/ooxml/pptx';
311
+
312
+ export const PptxViewerComponent = component$<{ src: string }>(({ src }) => {
313
+ const containerRef = useSignal<HTMLDivElement>();
314
+ const current = useSignal(0);
315
+ const total = useSignal(0);
316
+ let viewer: PptxViewerType | undefined;
317
+
318
+ // useVisibleTask$ runs only in the browser, never during SSR
319
+ useVisibleTask$(async () => {
320
+ if (!containerRef.value) return;
321
+ const { PptxViewer } = await import('@silurus/ooxml/pptx');
322
+ viewer = new PptxViewer(containerRef.value, {
323
+ onSlideChange: (i, t) => { current.value = i; total.value = t; },
324
+ });
325
+ const buf = await fetch(src).then(r => r.arrayBuffer());
326
+ await viewer.load(buf);
327
+ });
328
+
329
+ return (
330
+ <div>
331
+ <div ref={containerRef} style={{ width: '800px' }} />
332
+ <button onClick$={() => viewer?.prevSlide()}>‹ Prev</button>
333
+ <span> {current.value + 1} / {total.value} </span>
334
+ <button onClick$={() => viewer?.nextSlide()}>Next ›</button>
335
+ </div>
336
+ );
337
+ });
338
+ ```
339
+
340
+ </details>
341
+
342
+ ---
343
+
344
+ ## Feature Support
345
+
346
+ ### PowerPoint (.pptx)
347
+
348
+ | Category | Feature | Status |
349
+ |----------|---------|--------|
350
+ | **Slides** | Slide rendering | ✅ |
351
+ | | Slide layout / master inheritance | ✅ |
352
+ | | Slide size (custom dimensions) | ✅ |
353
+ | | Slide background (solid, gradient, image) | ✅ |
354
+ | | Slide numbers | ✅ |
355
+ | | Notes pages | ❌ |
356
+ | | Animations / transitions | ❌ |
357
+ | **Element types** | Shapes (`sp`) | ✅ |
358
+ | | Pictures (`pic`) | ✅ |
359
+ | | Groups (`grpSp`) with nested transforms | ✅ |
360
+ | | Connectors (`cxnSp`) | ✅ |
361
+ | | Tables (`tbl` in `graphicFrame`) | ✅ |
362
+ | | Charts (bar, line, area, radar, waterfall) | ✅ |
363
+ | | Charts (pie, scatter, bubble) | ❌ |
364
+ | | SmartArt | ❌ |
365
+ | | OLE objects | ❌ |
366
+ | | Video / audio | ❌ |
367
+ | **Shape geometry** | 130+ preset shapes (`prstGeom`) | ✅ |
368
+ | | Custom geometry (`custGeom`) | ✅ |
369
+ | | Rotation and flip (flipH / flipV) | ✅ |
370
+ | | 3D preset shapes | ❌ |
371
+ | **Fills** | Solid fill (`solidFill`) | ✅ |
372
+ | | Linear / radial gradient (`gradFill`) | ✅ |
373
+ | | No fill (`noFill`) | ✅ |
374
+ | | Pattern fill (`pattFill`) | ❌ |
375
+ | | Image fill on shapes (`blipFill` in `sp`) | ✅ |
376
+ | **Strokes** | Solid line color and width | ✅ |
377
+ | | Dash / dot styles | ✅ |
378
+ | | Arrow heads (`headEnd` / `tailEnd`) | ✅ |
379
+ | | Compound / double lines | ❌ |
380
+ | **Shape effects** | Drop shadow (`outerShdw`) | ✅ |
381
+ | | Inner shadow / glow / reflection | ❌ |
382
+ | | Bevel / 3D extrusion | ❌ |
383
+ | **Text — characters** | Bold, italic, underline, strikethrough | ✅ |
384
+ | | Font family, size, color | ✅ |
385
+ | | Superscript / subscript | ✅ |
386
+ | | Hyperlinks | ❌ |
387
+ | | Text shadow / outline effects | ❌ |
388
+ | **Text — paragraphs** | Horizontal alignment (left / center / right / justify) | ✅ |
389
+ | | Vertical anchor (top / center / bottom) | ✅ |
390
+ | | Line spacing (`spcPct`, `spcPts`) | ✅ |
391
+ | | Space before / after paragraph | ✅ |
392
+ | | Bullet points (character and auto-numbered) | ✅ |
393
+ | | Tab stops | ✅ |
394
+ | | Indent / margin | ✅ |
395
+ | | Vertical / RTL text | ❌ |
396
+ | **Text — body** | Text padding (insets) | ✅ |
397
+ | | normAutoFit (shrink to fit) | ✅ |
398
+ | | spAutoFit (expand box) | ✅ |
399
+ | | Word wrap / no wrap | ✅ |
400
+ | **Tables** | Cells, rows, columns | ✅ |
401
+ | | Cell merges (horizontal / vertical) | ✅ |
402
+ | | Cell borders | ✅ |
403
+ | | Cell fills (solid / gradient) | ✅ |
404
+ | | Cell diagonal lines (`lnTlToBr` / `lnBlToTr`) | ✅ |
405
+ | | Table theme styles | ❌ |
406
+ | **Theme** | Scheme colors (dk1/lt1/accent1–6) | ✅ |
407
+ | | Font scheme (`+mj-lt`, `+mn-lt`) | ✅ |
408
+ | | lumMod / lumOff / alpha transforms | ✅ |
409
+
410
+ ---
411
+
412
+ ### Word (.docx)
413
+
414
+ | Category | Feature | Status |
415
+ |----------|---------|--------|
416
+ | **Document** | Page rendering | ✅ |
417
+ | | Page size and margins | ✅ |
418
+ | | Headers / footers (default / first / even) | ✅ |
419
+ | | Section breaks | ❌ |
420
+ | **Text** | Paragraphs | ✅ |
421
+ | | Bold, italic, underline, strikethrough | ✅ |
422
+ | | Font family, size, color | ✅ |
423
+ | | Hyperlinks | ✅ |
424
+ | | Superscript / subscript | ❌ |
425
+ | **Formatting** | Paragraph alignment | ✅ |
426
+ | | Line spacing | ✅ |
427
+ | | Indents and tab stops | ✅ |
428
+ | | Lists (bullet and numbered) | ✅ |
429
+ | | Paragraph styles (Heading 1–6, Normal) | 🔜 Planned |
430
+ | **Elements** | Tables (with borders, fills, merges) | ✅ |
431
+ | | Images (inline and anchored) | ✅ |
432
+ | | Text boxes / drawing shapes | ❌ |
433
+ | **Advanced** | Track changes / comments / footnotes | ❌ |
434
+ | | Mail merge fields | ❌ Not planned |
435
+
436
+ ---
437
+
438
+ ### Excel (.xlsx)
439
+
440
+ | Category | Feature | Status |
441
+ |----------|---------|--------|
442
+ | **Workbook** | Multiple sheets, sheet names | ✅ |
443
+ | **Cells** | Text, number, boolean, error values | ✅ |
444
+ | | Formula results (from cached `<v>`) | ✅ |
445
+ | | Dates (ECMA-376 date format codes) | ✅ |
446
+ | | Rich text (per-run formatting) | ✅ |
447
+ | **Formatting** | Bold, italic, underline, strikethrough | ✅ |
448
+ | | Font family, size, color | ✅ |
449
+ | | Cell background color | ✅ |
450
+ | | Borders | ✅ |
451
+ | | Horizontal / vertical alignment | ✅ |
452
+ | | Text wrapping | ✅ |
453
+ | | Number formats (`0.00`, `%`, `#,##0`, custom date/time) | ✅ |
454
+ | **Structure** | Merged cells | ✅ |
455
+ | | Frozen panes | ✅ |
456
+ | | Row / column sizing (custom widths and heights) | ✅ |
457
+ | | Hidden rows / columns | ✅ |
458
+ | **Elements** | Images (`<xdr:twoCellAnchor>`) | ✅ |
459
+ | | Charts (bar, line, area, radar) | ✅ |
460
+ | | Sparklines | ❌ Not planned |
461
+ | **Advanced** | Conditional formatting (`cellIs`, `colorScale`, `dataBar`, `iconSet`, `top10`, `aboveAverage`) | ✅ |
462
+ | | Pivot tables | ❌ Not planned |
463
+ | | Data validation / comments | ❌ Not planned |
464
+
465
+ ---
466
+
467
+ ## Development
468
+
469
+ ```bash
470
+ # Install dependencies
471
+ pnpm install
472
+
473
+ # Build all WASM parsers (requires Rust + wasm-pack)
474
+ pnpm build:wasm
475
+
476
+ # Start Storybook dev server (port 6006)
477
+ pnpm storybook
478
+
479
+ # Type-check all packages
480
+ pnpm typecheck
481
+
482
+ # Run visual regression tests
483
+ pnpm vrt
484
+
485
+ # Build the library
486
+ pnpm build
487
+ ```
488
+
489
+ ### WASM build (individual packages)
490
+
491
+ ```bash
492
+ cd packages/pptx/parser && wasm-pack build --target web && cp pkg/pptx_parser_bg.wasm pkg/pptx_parser.js ../src/wasm/
493
+ cd packages/xlsx/parser && wasm-pack build --target web && cp pkg/xlsx_parser_bg.wasm pkg/xlsx_parser.js ../src/wasm/
494
+ cd packages/docx/parser && wasm-pack build --target web && cp pkg/docx_parser_bg.wasm pkg/docx_parser.js ../src/wasm/
495
+ ```
496
+
497
+ ## License
498
+
499
+ MIT
@@ -0,0 +1 @@
1
+ var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],enumerable:!0});return n||e(r,Symbol.toStringTag,{value:`Module`}),r};Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return t}});
@@ -0,0 +1,11 @@
1
+ //#region \0rolldown/runtime.js
2
+ var e = Object.defineProperty, t = (t, n) => {
3
+ let r = {};
4
+ for (var i in t) e(r, i, {
5
+ get: t[i],
6
+ enumerable: !0
7
+ });
8
+ return n || e(r, Symbol.toStringTag, { value: "Module" }), r;
9
+ };
10
+ //#endregion
11
+ export { t };