@file-viewer/ppt 0.3.0 → 0.3.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.
package/LICENSE CHANGED
@@ -1,196 +1,122 @@
1
- Flyfish Public Watermarked Binary License for @file-viewer/ppt
2
- Version 1.2, July 2026
3
- License policy identifier: flyfish-public-watermarked-binary-license-v1
1
+ Flyfish Public Watermarked Runtime License for @file-viewer/ppt
2
+ Version 2.0, July 2026
3
+ License policy identifier: flyfish-public-watermarked-binary-license-v2
4
4
 
5
5
  Copyright (c) 2026 Flyfish. All rights reserved except as expressly granted in this License.
6
6
 
7
- IMPORTANT: This is a proprietary binary-use license. It is not an open-source
8
- license. By downloading, installing, copying, accessing, or using the Public
9
- Package, You accept this License. If You do not accept it, do not use the Public
10
- Package and delete every copy under Your control.
7
+ IMPORTANT: This is a proprietary runtime license, not an open-source license.
8
+ By downloading, installing, copying, accessing, or using the Public Runtime,
9
+ You accept this License. If You do not accept it, do not use the Public Runtime.
11
10
 
12
11
  1. Definitions
13
12
 
14
- “Flyfish” means the copyright owner and licensor of the Public Package.
13
+ “Flyfish” means the copyright owner and licensor.
15
14
 
16
- “Public Package” means the official, unmodified @file-viewer/ppt binary npm
17
- package distributed by Flyfish under this License, including its JavaScript
18
- loader, native WebAssembly rendering module, declarations, documentation, and
19
- required Flyfish Viewer watermark.
15
+ “Public Runtime” means the official, unmodified @file-viewer/ppt public npm
16
+ package and its required JavaScript, Worker, WebAssembly, font, declaration,
17
+ manifest, documentation, notice, and Flyfish Viewer watermark files.
20
18
 
21
- “You” means an individual natural person acting solely in that person’s
22
- personal capacity. A company, legal entity, government body, school, nonprofit,
23
- team, employer, client, or other organization is not “You” under the free grant,
24
- even when an individual accesses the Public Package on its behalf.
19
+ “You” means any individual or organization using or distributing the Public
20
+ Runtime under this License.
25
21
 
26
- Personal Non-Commercial Use” means learning, private research, private testing,
27
- personal evaluation, or a personal non-revenue project that is not performed
28
- for, on behalf of, or in connection with any organization, employment, client,
29
- profession, trade, business, compensation, fee, fundraising, or commercial
30
- advantage.
22
+ Integrated Product” means an application, website, service, library, demo,
23
+ deployment bundle, or other product in which the Public Runtime is included as
24
+ a functional dependency for PowerPoint preview rather than offered as a
25
+ standalone substitute for @file-viewer/ppt.
31
26
 
32
- “Commercial Use” means every use that is not Personal Non-Commercial Use.
27
+ 2. Public watermarked grant
33
28
 
34
- 2. Limited free grant
29
+ Subject to this License, Flyfish grants You a worldwide, royalty-free,
30
+ non-exclusive, non-transferable license to:
35
31
 
36
- Subject to continuous compliance with this License, Flyfish grants You a
37
- limited, non-exclusive, non-transferable, non-sublicensable, revocable right to:
32
+ - download, install, copy, and execute the Public Runtime for any lawful
33
+ personal, organizational, educational, government, nonprofit, internal,
34
+ commercial, production, SaaS, hosted, customer-facing, or professional use;
35
+ - integrate and deploy the Public Runtime in an Integrated Product;
36
+ - bundle or copy the unmodified Public Runtime through npm, package managers,
37
+ build tools, private registries, static asset directories, Docker images,
38
+ offline installers, demos, Full packages, CDN/IIFE distributions,
39
+ copy-assets workflows, and GitHub Release artifacts;
40
+ - host, mirror, and redistribute the unmodified Public Runtime when reasonably
41
+ required to install, operate, deploy, or distribute an Integrated Product;
42
+ and
43
+ - charge for an Integrated Product or service that includes the Public Runtime.
38
44
 
39
- - download and install the official Public Package;
40
- - execute it on any normal HTTP or HTTPS browser origin solely for Personal
41
- Non-Commercial Use; and
42
- - serve unmodified Public Package assets only as an integrated component of
43
- Your own Personal Non-Commercial Use project.
45
+ No paid license is required for these uses while the required Flyfish Viewer
46
+ watermark remains visible, complete, unobstructed, and unmodified in every
47
+ rendered slide and preview.
44
48
 
45
- The grant is conditioned on the Flyfish Viewer watermark remaining visible,
46
- complete, unobstructed, and unmodified in every rendered slide and preview.
47
- The grant does not include source code, source-delivery rights, redistribution
48
- rights, modification rights, derivative-work rights, or any commercial right.
49
+ Routine technical bundling, tree-shaking, compression, content hashing, and
50
+ minification are permitted when they do not change runtime behavior, bypass
51
+ integrity checks, remove notices, or alter the required watermark.
49
52
 
50
- 3. Commercial and organizational use
53
+ 3. Watermark-free commercial authorization
51
54
 
52
- You must obtain a separate written commercial license from Flyfish before any
53
- Commercial Use. Commercial Use includes, without limitation:
55
+ A separate written commercial authorization is required only to remove,
56
+ replace, hide, crop, cover, weaken, restyle, or otherwise alter the required
57
+ Flyfish Viewer watermark, or to receive a separately built watermark-free
58
+ runtime or source delivery. Commercial authorization applies only to the
59
+ entities, deployments, features, origins, term, and other scope stated in that
60
+ agreement.
54
61
 
55
- - any use by or for a company, organization, government body, school,
56
- nonprofit, team, employer, customer, or client, including internal evaluation;
57
- - production, intranet, extranet, SaaS, hosted, cloud, embedded, OEM, resale,
58
- consulting, customer-delivery, revenue-generating, fee-supported, sponsored,
59
- or professionally connected use;
60
- - integration into any product, service, project, procurement, sales demo, or
61
- workflow connected with business or compensated activity; and
62
- - any removal, hiding, obstruction, weakening, alteration, or replacement of
63
- the required Flyfish Viewer watermark or public-edition restrictions.
64
-
65
- A separately purchased commercial or enterprise agreement applies only to the
66
- features, deployments, origins, entities, term, and other scope expressly stated
67
- in that agreement. A commercial agreement does not amend this Public Package.
68
- Flyfish may instead provide a separately built commercial package.
69
-
70
- Purchase and authorization information:
71
- https://dev.flyfish.group/shop
62
+ Authorization information: https://dev.flyfish.group/shop
72
63
 
73
64
  4. Restrictions
74
65
 
75
- Except to the limited extent that mandatory law expressly permits despite this
76
- restriction, You may not, and may not enable or assist another person to:
77
-
78
- - redistribute, publish, mirror, sublicense, sell, rent, lease, transfer,
79
- repackage, or make the Public Package available as a standalone download,
80
- reusable dependency, CDN asset, hosted API, or service for third parties;
81
- - modify, translate, adapt, patch, or create derivative works from the Public
82
- Package, WebAssembly module, or watermark;
66
+ Except to the limited extent mandatory law permits despite these restrictions,
67
+ You may not, and may not enable another person to:
68
+
69
+ - sell, rent, sublicense, rebrand, rename, or publish the Public Runtime as a
70
+ standalone or competing package, download, API, hosted conversion service,
71
+ or product that substitutes for the official @file-viewer/ppt package;
72
+ - represent the Public Runtime as Your own engine, as open source, or as
73
+ licensed under Apache-2.0, MIT, BSD, GPL, or another open-source license;
74
+ - modify, translate, patch, adapt, or create derivative works from the Public
75
+ Runtime except for the routine integration transformations expressly allowed
76
+ in Section 2;
83
77
  - reverse engineer, decrypt, decompile, disassemble, extract, dump, recover, or
84
- otherwise attempt to derive source code, keys, protected modules, algorithms,
85
- or non-public interfaces;
86
- - bypass, forge, replace, disable, or interfere with any policy gate, integrity
87
- check, encryption, feature restriction, watermark control, or other technical
88
- protection measure;
78
+ attempt to derive source code, keys, protected modules, algorithms, or
79
+ non-public interfaces;
80
+ - bypass, forge, replace, disable, or interfere with a policy gate, integrity
81
+ check, encryption measure, feature restriction, watermark control, or other
82
+ technical protection;
89
83
  - remove, hide, crop, cover, restyle, obscure, weaken, or interfere with the
90
- Flyfish Viewer watermark in the viewer output;
84
+ required Flyfish Viewer watermark without separate written authorization; or
91
85
  - remove or alter copyright, license, attribution, provenance, trademark, or
92
- commercial-use notices; or
93
- - represent the Public Package as open source or as licensed under Apache-2.0,
94
- MIT, BSD, GPL, or another open-source license.
95
-
96
- Installing the official package through npm and making reasonable backup copies
97
- solely for Your permitted use are not prohibited redistribution.
98
-
99
- Technical bundling, minification, and static-asset deployment are permitted only
100
- as necessary to operate Your own project within the Limited Free Grant, provided
101
- that the Public Package is not exposed or supplied as a reusable library,
102
- standalone dependency, mirror, download, API, or service, and provided that all
103
- license notices, integrity controls, and required watermarks remain intact.
104
-
105
- 5. Separate source deliveries and Apache-2.0 boundary
106
-
107
- This License applies only to the Public Package and does not grant rights under
108
- the Apache License 2.0 or any other open-source license.
109
-
110
- Any Apache License 2.0 terms that Flyfish may separately provide to a specific
111
- source-delivery customer apply only to the source files and scope expressly
112
- identified in that separate delivery. Those terms are not incorporated into,
113
- and do not extend to, this Public Package, its public WebAssembly distribution,
114
- the Flyfish Viewer watermark, or Flyfish trademarks.
115
- Nothing in this License changes rights already validly granted in a separate
116
- written delivery or license agreement.
117
-
118
- 6. Watermark and limited trademark permission
119
-
120
- The Flyfish name and Flyfish Viewer watermark are protected marks of Flyfish.
121
- Solely so You can comply with this License, Flyfish grants You a narrow,
122
- non-transferable permission to display the unmodified mandatory watermark as
123
- produced by the Public Package. No other trademark, branding, endorsement, or
124
- merchandising right is granted.
125
-
126
- Only a separately purchased and separately built commercial package may omit
127
- the public watermark. Possession of a license file, source delivery, package
128
- copy, or modified runtime does not by itself authorize watermark removal.
129
-
130
- 7. Ownership and reservation of rights
131
-
132
- Flyfish and its licensors retain all title, ownership, copyright, patent,
133
- trade-secret, trademark, and other intellectual-property rights in the Public
134
- Package. No right is granted by implication, estoppel, exhaustion, or otherwise.
135
- All rights not expressly granted are reserved.
136
-
137
- Third-party components, if expressly identified in accompanying notices, remain
138
- subject to their respective licenses solely for those identified components.
139
- No third-party notice expands the grant for Flyfish proprietary components.
140
-
141
- 8. Updates and policy changes
142
-
143
- Each version of the Public Package is governed by the LICENSE file shipped with
144
- that version. Flyfish may offer later versions under revised terms. Continuing
145
- to use an updated version constitutes acceptance of the terms shipped with that
146
- updated version.
147
-
148
- 9. Termination
149
-
150
- This License terminates automatically and immediately upon breach. On
151
- termination, You must stop all use and delete every copy under Your possession
152
- or control. Sections 3 through 7 and 9 through 11 survive termination.
153
- Termination does not limit any other remedy available to Flyfish.
154
-
155
- 10. Disclaimer and limitation of liability
156
-
157
- THE PUBLIC PACKAGE IS PROVIDED “AS IS” AND “AS AVAILABLE,” WITHOUT WARRANTIES OR
158
- CONDITIONS OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING TITLE, NON-INFRINGEMENT,
159
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, AVAILABILITY, OR
160
- SECURITY. TO THE MAXIMUM EXTENT PERMITTED BY LAW, FLYFISH AND ITS LICENSORS WILL
161
- NOT BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, EXEMPLARY,
162
- PUNITIVE, OR COMMERCIAL DAMAGES, OR FOR LOSS OF DATA, PROFITS, REVENUE, GOODWILL,
163
- OR BUSINESS INTERRUPTION, ARISING FROM OR RELATED TO THE PUBLIC PACKAGE OR THIS
164
- LICENSE.
165
-
166
- 11. General
167
-
168
- This License is the complete agreement for the Public Package unless You and
169
- Flyfish have signed a separate written agreement that expressly governs the
170
- same use. If a provision is unenforceable, it will be limited to the minimum
171
- extent necessary and the remaining provisions will continue in effect. A waiver
172
- must be in writing and applies only to the stated instance. You may not assign
173
- this License. Headings are for convenience only.
174
-
175
- 12. Contact
176
-
177
- Commercial licensing: https://dev.flyfish.group/shop
178
- Support email: wybaby168@gmail.com
179
-
180
- 中文摘要(仅便于阅读;如与英文条款冲突,以英文条款为准)
181
-
182
- - 本 npm 公网包是 Flyfish 专有二进制发行版,不是开源软件,也不适用
183
- Apache-2.0。单独向特定源码客户交付的 Apache-2.0 条款,仅适用于该次交付中
184
- 明确列出的源码和范围,不会扩展到本公网 npm 包。
185
- - 免费授权对象仅为以个人身份行事的自然人,且仅限个人、非商用、非组织用途;
186
- 公司、组织、政府、学校、非营利机构、团队、雇主或客户的内部评估也属于商用。
187
- - 公网水印版不限制正常 HTTP/HTTPS origin,但每一页必须完整显示
188
- Flyfish Viewer 水印。不得删除、遮挡、裁剪、弱化或替换水印。
189
- - 不得再分发、镜像、转售、重打包、修改、反编译、解密、提取、伪造授权或绕过
190
- WASM、完整性校验、功能限制及其他技术保护措施。
191
- - 在免费授权范围内,仅为运行本人项目而进行必要的前端打包、压缩和静态资源部署
192
- 是允许的,但不得将本包作为可复用依赖、镜像、独立下载、API 或服务向第三方
193
- 提供,并且必须完整保留许可证声明、完整性保护和水印。
194
- - 商业、生产、内网、SaaS、托管、嵌入、OEM、客户项目、收费或其他业务用途,
195
- 必须先购买单独商业授权;只有单独构建的商业包可以不显示公网水印。
196
- - 商业授权购买地址:https://dev.flyfish.group/shop
86
+ third-party notices.
87
+
88
+ An exact official package mirror, cache, backup, or static copy used to install
89
+ or deploy an Integrated Product is not prohibited standalone redistribution.
90
+
91
+ 5. Proprietary and open-source boundary
92
+
93
+ This License applies only to the Public Runtime. It does not grant source code,
94
+ source-delivery, patent, trademark, or open-source rights. Apache-2.0 terms that
95
+ may apply to File Viewer or to a separately identified source delivery do not
96
+ extend to @file-viewer/ppt, its WebAssembly runtime, or Flyfish trademarks.
97
+
98
+ Third-party components identified in the NOTICE remain subject to their own
99
+ licenses. Those notices do not expand the grant for Flyfish proprietary code.
100
+
101
+ 6. Ownership and trademark
102
+
103
+ Flyfish and its licensors retain all title and intellectual-property rights in
104
+ the Public Runtime. The narrow permission to display the unmodified Flyfish
105
+ Viewer watermark does not grant any other trademark or endorsement right.
106
+
107
+ 7. Termination
108
+
109
+ This License terminates automatically if You materially violate it. Your rights
110
+ are reinstated when You cure the violation within 30 days after discovery,
111
+ unless Flyfish has notified You of a repeated or intentional violation.
112
+
113
+ 8. Disclaimer and limitation of liability
114
+
115
+ THE PUBLIC RUNTIME IS PROVIDED “AS IS”, WITHOUT WARRANTIES OR CONDITIONS OF ANY
116
+ KIND. TO THE MAXIMUM EXTENT PERMITTED BY LAW, FLYFISH IS NOT LIABLE FOR INDIRECT,
117
+ INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR EXEMPLARY DAMAGES ARISING FROM ITS USE.
118
+
119
+ 9. Entire license
120
+
121
+ This License is the complete public grant for the Public Runtime. A separate
122
+ written agreement controls only where it expressly grants additional rights.
package/NOTICE CHANGED
@@ -1,29 +1,28 @@
1
1
  @file-viewer/ppt
2
2
  Copyright 2026 Flyfish.
3
3
 
4
- PROPRIETARY PUBLIC BINARY DISTRIBUTION — NOT OPEN SOURCE
4
+ PROPRIETARY PUBLIC WATERMARKED RUNTIME — NOT OPEN SOURCE
5
5
 
6
- This npm package is governed only by the included Flyfish Public Watermarked
7
- Binary License. It is not licensed under Apache-2.0. Apache-2.0 terms supplied
8
- under a separate written source-code delivery apply only to the files and scope
9
- expressly identified in that separate delivery and do not extend to this npm
10
- package.
6
+ This npm package is governed by the included Flyfish Public Watermarked Runtime
7
+ License. It is not licensed under Apache-2.0. Apache-2.0 terms that may apply to
8
+ File Viewer or a separately identified source delivery do not extend to this
9
+ runtime.
11
10
 
12
- Free use is limited to an individual natural person acting solely in a personal,
13
- non-commercial, non-organizational capacity. Use by or for any company,
14
- organization, government body, school, nonprofit, team, employer, customer, or
15
- client requires a purchased commercial license.
11
+ The unmodified public runtime may be used by individuals and organizations for
12
+ personal, internal, commercial, production, SaaS, hosted, and customer-facing
13
+ applications. It may be bundled, copied, hosted, mirrored, and redistributed as
14
+ an integrated dependency of File Viewer or another application, including npm,
15
+ Full, CDN/IIFE, Docker, offline, copy-assets, and GitHub Release distributions.
16
16
 
17
17
  Every public rendering must display the unmodified Flyfish Viewer watermark.
18
- Only a separately purchased and separately built commercial package may omit
19
- that watermark.
18
+ Only separate written commercial authorization may permit removing, replacing,
19
+ hiding, or weakening that watermark or using a watermark-free build.
20
20
 
21
- This package contains a native PPT parsing and Canvas rasterization WebAssembly
22
- runtime plus generated public declarations. It does not include a customer
23
- runtime license, license-signing secret, commercial watermark-removal
24
- capability, source-delivery rights, or readable parser/renderer source.
21
+ The package contains a native PPT parsing and Canvas rasterization WebAssembly
22
+ runtime plus generated public declarations. It does not include source-delivery
23
+ rights, watermark-removal capability, or readable parser/renderer source.
25
24
 
26
- Commercial licensing: https://dev.flyfish.group/shop
25
+ Commercial watermark authorization: https://dev.flyfish.group/shop
27
26
 
28
27
  The package ships deterministic fallback font resources separately from the
29
28
  native engine. Their copyright notices and full font licenses are appended to
@@ -63,7 +62,6 @@ requirement for fonts to remain under this license does not apply to
63
62
  any document created using the fonts or their derivatives.
64
63
 
65
64
 
66
-
67
65
  DEFINITIONS
68
66
  "Font Software" refers to the set of files released by the Copyright
69
67
  Holder(s) under this license and clearly marked as such.
@@ -119,12 +117,10 @@ Software, subject to the following conditions:
119
117
  Software.
120
118
 
121
119
 
122
-
123
120
  TERMINATION
124
121
  This license becomes null and void if any of the above conditions are not met.
125
122
 
126
123
 
127
-
128
124
  DISCLAIMER
129
125
  THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
130
126
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
package/README.md CHANGED
@@ -193,9 +193,9 @@ moves assets, configure them explicitly:
193
193
 
194
194
  ```js
195
195
  const viewer = await createPptViewer({
196
- workerUrl: new URL('/vendor/file-viewer/ppt/0.3.0/worker.mjs', location.origin),
197
- wasmUrl: new URL('/vendor/file-viewer/ppt/0.3.0/ppt-native.wasm', location.origin),
198
- fontUrl: new URL('/vendor/file-viewer/ppt/0.3.0/ppt-font-cjk.otf', location.origin)
196
+ workerUrl: new URL('/vendor/file-viewer/ppt/0.3.2/worker.mjs', location.origin),
197
+ wasmUrl: new URL('/vendor/file-viewer/ppt/0.3.2/ppt-native.wasm', location.origin),
198
+ fontUrl: new URL('/vendor/file-viewer/ppt/0.3.2/ppt-font-cjk.otf', location.origin)
199
199
  });
200
200
  ```
201
201
 
@@ -268,18 +268,20 @@ be used when a document font is not available to the viewer.
268
268
 
269
269
  | Edition | Permitted use | Watermark | Origin |
270
270
  | --- | --- | --- | --- |
271
- | Public npm edition | Individual personal, non-commercial, non-organizational use | Mandatory `Flyfish Viewer` on every slide | Any normal HTTP/HTTPS origin |
272
- | Commercial edition | Licensed company, organization, customer project, intranet, SaaS, OEM, or production use | Watermark-free build available according to the purchased agreement | According to the commercial agreement |
271
+ | Public npm edition | Any lawful personal, organizational, commercial, production, SaaS, hosted, or customer-facing use | Mandatory `Flyfish Viewer` on every slide | Any normal HTTP/HTTPS origin |
272
+ | Watermark-free edition | Uses covered by a separate written authorization | Watermark-free build according to the purchased agreement | According to the commercial agreement |
273
273
 
274
- **Commercial use requires a purchased license.** Use by or for a company,
275
- organization, government body, school, nonprofit, team, employer, customer, or
276
- client—including internal evaluation—is commercial use.
274
+ The unmodified public runtime can be bundled, self-hosted, and redistributed as
275
+ an integrated dependency, including File Viewer Full, CDN/IIFE, Docker, offline,
276
+ copy-assets, and GitHub Release distributions. A paid authorization is required
277
+ only to remove, replace, hide, or weaken the required watermark, or to receive a
278
+ separate watermark-free build or source delivery.
277
279
 
278
280
  The public npm edition is proprietary software and is **not licensed under
279
281
  Apache-2.0**. Removing, hiding, covering, replacing, or bypassing the required
280
282
  watermark is prohibited. See [LICENSE](./LICENSE) for the binding terms.
281
283
 
282
- Purchase a commercial license from the
284
+ Purchase watermark-free commercial authorization from the
283
285
  [Flyfish Shop](https://dev.flyfish.group/shop).
284
286
 
285
287
  ### Product links
@@ -445,7 +447,7 @@ manifest.json
445
447
  `application/wasm`,`.otf` 使用 `font/otf`,`.ppt` 使用
446
448
  `application/vnd.ms-powerpoint`。
447
449
 
448
- 将资源发布在版本化或内容寻址目录(如 `/vendor/file-viewer/ppt/0.3.0/`)后,
450
+ 将资源发布在版本化或内容寻址目录(如 `/vendor/file-viewer/ppt/0.3.2/`)后,
449
451
  可配置:
450
452
 
451
453
  ```http
@@ -477,19 +479,20 @@ Cache-Control: public, max-age=31536000, immutable
477
479
 
478
480
  | 版本 | 允许用途 | 水印 | Origin |
479
481
  | --- | --- | --- | --- |
480
- | 公网 npm 版 | 自然人个人、非商业、非组织用途 | 每页强制显示 `Flyfish Viewer` | 任意正常 HTTP/HTTPS origin |
481
- | 商业版 | 已授权的公司、组织、客户项目、内网、SaaS、OEM 或生产用途 | 按商业协议提供无公网水印构建 | 按商业协议执行 |
482
+ | 公网 npm 版 | 任何合法的个人、组织、商业、生产、SaaS、内网或客户项目用途 | 每页强制显示 `Flyfish Viewer` | 任意正常 HTTP/HTTPS origin |
483
+ | 无水印版 | 书面商业授权覆盖的用途 | 按协议提供无水印构建 | 按商业协议执行 |
482
484
 
483
- **任何商业用途均需购买授权。** 公司、组织、政府、学校、非营利机构、团队、
484
- 雇主、客户或委托项目的使用,包括内部评估,均属于商业用途。
485
+ 未修改的公网运行时可以作为应用依赖打包、自托管和再分发,包括 File Viewer Full、
486
+ CDN/IIFE、Docker、离线包、copy-assets 和 GitHub Release。只有移除、替换、隐藏或
487
+ 弱化强制水印,或取得单独的无水印构建/源码交付时,才需要书面商业授权。
485
488
 
486
489
  公网 npm 版是专有软件,**不适用 Apache-2.0**。禁止删除、隐藏、遮挡、替换或
487
490
  绕过强制水印。具有法律约束力的完整条款见 [LICENSE](./LICENSE)。
488
491
 
489
- 商业授权请前往 [Flyfish 商店](https://dev.flyfish.group/shop)购买。
492
+ 无水印商业授权请前往 [Flyfish 商店](https://dev.flyfish.group/shop)购买。
490
493
 
491
494
  ### 产品链接
492
495
 
493
496
  - [产品文档](https://officejs-doc.pages.dev)
494
- - [商业授权](https://dev.flyfish.group/shop)
497
+ - [无水印商业授权](https://dev.flyfish.group/shop)
495
498
  - [问题反馈](https://github.com/flyfish-dev/office-preview-js/issues)
package/index.d.ts CHANGED
@@ -16,18 +16,21 @@ export interface PptPackageManifest {
16
16
  readonly id: 'FV-PPT-PUBLIC-WATERMARKED-V2';
17
17
  readonly feature: 'ppt';
18
18
  readonly edition: 'public-watermarked';
19
- readonly holder: 'Individual Personal Non-Commercial Use';
19
+ readonly holder: 'Individuals and Organizations';
20
20
  readonly distribution: 'proprietary-public-binary';
21
- readonly commercialUse: false;
21
+ readonly commercialUse: true;
22
22
  readonly publicAnyOrigin: true;
23
23
  readonly watermarkRequired: true;
24
24
  readonly watermarkText: 'Flyfish Viewer';
25
- readonly usePolicy: 'public-individual-noncommercial-watermarked';
26
- readonly licensePolicy: 'flyfish-public-watermarked-binary-license-v1';
25
+ readonly usePolicy: 'public-watermarked-general-use';
26
+ readonly licensePolicy: 'flyfish-public-watermarked-binary-license-v2';
27
27
  readonly sourceCodeRights: false;
28
28
  readonly sourceDeliveryRights: false;
29
29
  readonly apacheLicenseApplies: false;
30
- readonly redistributionAllowed: false;
30
+ readonly redistributionAllowed: true;
31
+ readonly redistributionPolicy: 'unmodified-integrated-bundling-only';
32
+ readonly standaloneRedistributionAllowed: false;
33
+ readonly watermarkRemovalRequiresCommercialAuthorization: true;
31
34
  readonly runtimeLicenseRequired: false;
32
35
  readonly renderer: 'native-wasm-worker-offscreen-canvas';
33
36
  readonly integrity: 'native-wasm-font-sha256-v2';
@@ -45,13 +48,16 @@ export interface PptPackageManifest {
45
48
  export interface PptRuntimeLicenseSummary {
46
49
  readonly id: 'FV-PPT-PUBLIC-WATERMARKED-V2';
47
50
  readonly edition: 'public-watermarked';
48
- readonly holder: 'Individual Personal Non-Commercial Use';
49
- readonly policy: 'flyfish-public-watermarked-binary-license-v1';
50
- readonly commercialUse: false;
51
+ readonly holder: 'Individuals and Organizations';
52
+ readonly policy: 'flyfish-public-watermarked-binary-license-v2';
53
+ readonly commercialUse: true;
51
54
  readonly sourceCodeRights: false;
52
55
  readonly sourceDeliveryRights: false;
53
56
  readonly apacheLicenseApplies: false;
54
- readonly redistributionAllowed: false;
57
+ readonly redistributionAllowed: true;
58
+ readonly redistributionPolicy: 'unmodified-integrated-bundling-only';
59
+ readonly standaloneRedistributionAllowed: false;
60
+ readonly watermarkRemovalRequiresCommercialAuthorization: true;
55
61
  }
56
62
 
57
63
  export interface PptWatermarkSummary {
package/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- const MANIFEST = Object.freeze({"kind":"flyfish-ppt-public-native-wasm-v2","product":"Flyfish PPT Viewer","format":"PowerPoint 97-2003 (.ppt)","packageName":"@file-viewer/ppt","packageVersion":"0.3.0","engine":"flyfish-classic-ppt-native-engine","engineBuild":"28e655e6a05c9b73","id":"FV-PPT-PUBLIC-WATERMARKED-V2","feature":"ppt","edition":"public-watermarked","holder":"Individual Personal Non-Commercial Use","distribution":"proprietary-public-binary","commercialUse":false,"publicAnyOrigin":true,"watermarkRequired":true,"watermarkText":"Flyfish Viewer","usePolicy":"public-individual-noncommercial-watermarked","licensePolicy":"flyfish-public-watermarked-binary-license-v1","sourceCodeRights":false,"sourceDeliveryRights":false,"apacheLicenseApplies":false,"redistributionAllowed":false,"runtimeLicenseRequired":false,"renderer":"native-wasm-worker-offscreen-canvas","integrity":"native-wasm-font-sha256-v2","wasmFile":"ppt-native.wasm","wasmBytes":1557607,"workerFile":"worker.mjs","fontPack":{"kind":"flyfish-cjk-font-pack-v1","file":"ppt-font-cjk.otf","bytes":16437364,"sha256":"2c76254f6fc379fddfce0a7e84fb5385bb135d3e399294f6eeb6680d0365b74b"},"workerDefault":true,"virtualScrollDefault":true,"frameCache":"indexeddb-final-watermarked-png-lru","watermarkEnforcement":"native-final-frame","wasmSha256":"c14dd1d5b971817b2eb133739fe928920b9ccc0a5afb25f7044d879bed7c6d23"});
1
+ const MANIFEST = Object.freeze({"kind":"flyfish-ppt-public-native-wasm-v2","product":"Flyfish PPT Viewer","format":"PowerPoint 97-2003 (.ppt)","packageName":"@file-viewer/ppt","packageVersion":"0.3.2","engine":"flyfish-classic-ppt-native-engine","engineBuild":"ee7c677fc3007f1b","id":"FV-PPT-PUBLIC-WATERMARKED-V2","feature":"ppt","edition":"public-watermarked","holder":"Individuals and Organizations","distribution":"proprietary-public-binary","commercialUse":true,"publicAnyOrigin":true,"watermarkRequired":true,"watermarkText":"Flyfish Viewer","usePolicy":"public-watermarked-general-use","licensePolicy":"flyfish-public-watermarked-binary-license-v2","sourceCodeRights":false,"sourceDeliveryRights":false,"apacheLicenseApplies":false,"redistributionAllowed":true,"redistributionPolicy":"unmodified-integrated-bundling-only","standaloneRedistributionAllowed":false,"watermarkRemovalRequiresCommercialAuthorization":true,"runtimeLicenseRequired":false,"renderer":"native-wasm-worker-offscreen-canvas","integrity":"native-wasm-font-sha256-v2","wasmFile":"ppt-native.wasm","wasmBytes":1557871,"workerFile":"worker.mjs","fontPack":{"kind":"flyfish-cjk-font-pack-v1","file":"ppt-font-cjk.otf","bytes":16437364,"sha256":"2c76254f6fc379fddfce0a7e84fb5385bb135d3e399294f6eeb6680d0365b74b"},"workerDefault":true,"virtualScrollDefault":true,"frameCache":"indexeddb-final-watermarked-png-lru","watermarkEnforcement":"native-final-frame","wasmSha256":"6d209f487ab8be74f23b463eaecafd9f588948e75670f82e69d7b7d6b10cb6a7"});
2
2
  const DEFAULT_WASM_URL = new URL('./ppt-native.wasm', import.meta.url);
3
3
  const DEFAULT_FONT_URL = new URL(`./${MANIFEST.fontPack.file}`, import.meta.url);
4
4
  const DEFAULT_WORKER_URL = new URL(`./${MANIFEST.workerFile}`, import.meta.url);
@@ -26,13 +26,16 @@ const WASM_EXPORTS = Object.freeze([
26
26
  const LICENSE = Object.freeze({
27
27
  id: 'FV-PPT-PUBLIC-WATERMARKED-V2',
28
28
  edition: 'public-watermarked',
29
- holder: 'Individual Personal Non-Commercial Use',
30
- policy: 'flyfish-public-watermarked-binary-license-v1',
31
- commercialUse: false,
29
+ holder: 'Individuals and Organizations',
30
+ policy: 'flyfish-public-watermarked-binary-license-v2',
31
+ commercialUse: true,
32
32
  sourceCodeRights: false,
33
33
  sourceDeliveryRights: false,
34
34
  apacheLicenseApplies: false,
35
- redistributionAllowed: false
35
+ redistributionAllowed: true,
36
+ redistributionPolicy: 'unmodified-integrated-bundling-only',
37
+ standaloneRedistributionAllowed: false,
38
+ watermarkRemovalRequiresCommercialAuthorization: true
36
39
  });
37
40
 
38
41
  const WATERMARK = Object.freeze({ required: true, text: 'Flyfish Viewer' });
package/manifest.json CHANGED
@@ -3,29 +3,32 @@
3
3
  "product": "Flyfish PPT Viewer",
4
4
  "format": "PowerPoint 97-2003 (.ppt)",
5
5
  "packageName": "@file-viewer/ppt",
6
- "packageVersion": "0.3.0",
6
+ "packageVersion": "0.3.2",
7
7
  "engine": "flyfish-classic-ppt-native-engine",
8
- "engineBuild": "28e655e6a05c9b73",
8
+ "engineBuild": "ee7c677fc3007f1b",
9
9
  "id": "FV-PPT-PUBLIC-WATERMARKED-V2",
10
10
  "feature": "ppt",
11
11
  "edition": "public-watermarked",
12
- "holder": "Individual Personal Non-Commercial Use",
12
+ "holder": "Individuals and Organizations",
13
13
  "distribution": "proprietary-public-binary",
14
- "commercialUse": false,
14
+ "commercialUse": true,
15
15
  "publicAnyOrigin": true,
16
16
  "watermarkRequired": true,
17
17
  "watermarkText": "Flyfish Viewer",
18
- "usePolicy": "public-individual-noncommercial-watermarked",
19
- "licensePolicy": "flyfish-public-watermarked-binary-license-v1",
18
+ "usePolicy": "public-watermarked-general-use",
19
+ "licensePolicy": "flyfish-public-watermarked-binary-license-v2",
20
20
  "sourceCodeRights": false,
21
21
  "sourceDeliveryRights": false,
22
22
  "apacheLicenseApplies": false,
23
- "redistributionAllowed": false,
23
+ "redistributionAllowed": true,
24
+ "redistributionPolicy": "unmodified-integrated-bundling-only",
25
+ "standaloneRedistributionAllowed": false,
26
+ "watermarkRemovalRequiresCommercialAuthorization": true,
24
27
  "runtimeLicenseRequired": false,
25
28
  "renderer": "native-wasm-worker-offscreen-canvas",
26
29
  "integrity": "native-wasm-font-sha256-v2",
27
30
  "wasmFile": "ppt-native.wasm",
28
- "wasmBytes": 1557607,
31
+ "wasmBytes": 1557871,
29
32
  "workerFile": "worker.mjs",
30
33
  "fontPack": {
31
34
  "kind": "flyfish-cjk-font-pack-v1",
@@ -37,5 +40,5 @@
37
40
  "virtualScrollDefault": true,
38
41
  "frameCache": "indexeddb-final-watermarked-png-lru",
39
42
  "watermarkEnforcement": "native-final-frame",
40
- "wasmSha256": "c14dd1d5b971817b2eb133739fe928920b9ccc0a5afb25f7044d879bed7c6d23"
43
+ "wasmSha256": "6d209f487ab8be74f23b463eaecafd9f588948e75670f82e69d7b7d6b10cb6a7"
41
44
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@file-viewer/ppt",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Professional native WebAssembly canvas viewer for PowerPoint 97-2003 (.ppt) files.",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",
package/ppt-native.wasm CHANGED
Binary file
package/worker.mjs CHANGED
@@ -1,4 +1,734 @@
1
- import { createFrameCache } from './frame-cache.mjs';
1
+ /**
2
+ * Bounded, best-effort cache for final watermarked PNG frames.
3
+ *
4
+ * This module is intentionally storage-only. It does not parse PPT data, inspect
5
+ * font assets, or transform renderer output. Every storage failure degrades to a
6
+ * cache miss so IndexedDB can never become a rendering dependency.
7
+ */
8
+
9
+ const DB_VERSION = 1;
10
+ const FRAME_STORE = "frames";
11
+ const META_STORE = "meta";
12
+ const DOCUMENT_INDEX = "byDocument";
13
+ const ACCESS_INDEX = "byLastAccess";
14
+ const USAGE_KEY = "usage";
15
+
16
+ const MIB = 1024 * 1024;
17
+ const DEFAULT_DB_NAME = "flyfish-ppt-frame-cache-v1";
18
+ const DEFAULT_MAX_BYTES = 256 * MIB;
19
+ const DEFAULT_MAX_ENTRIES = 256;
20
+ const DEFAULT_MAX_ITEM_BYTES = 32 * MIB;
21
+ const MAX_DATABASE_NAME_LENGTH = 128;
22
+ const MAX_DOCUMENT_ID_LENGTH = 256;
23
+ const MAX_VARIANT_LENGTH = 256;
24
+ const MAX_PAGE_INDEX = 10_000_000;
25
+ const MAX_DIMENSION = 65_535;
26
+ const OPEN_TIMEOUT_MS = 1_000;
27
+
28
+ function clampInteger(value, fallback, minimum, maximum) {
29
+ let numeric;
30
+ try {
31
+ numeric = Number(value);
32
+ } catch {
33
+ return fallback;
34
+ }
35
+ if (!Number.isFinite(numeric)) {
36
+ return fallback;
37
+ }
38
+ return Math.min(maximum, Math.max(minimum, Math.trunc(numeric)));
39
+ }
40
+
41
+ function normalizeBoundedString(value, maximumLength, { trim = false } = {}) {
42
+ if (typeof value !== "string") {
43
+ return null;
44
+ }
45
+ const normalized = trim ? value.trim() : value;
46
+ if (normalized.length === 0 || normalized.length > maximumLength) {
47
+ return null;
48
+ }
49
+ return normalized;
50
+ }
51
+
52
+ /**
53
+ * Pure limit normalization used by both production code and unit tests.
54
+ */
55
+ function normalizeFrameCacheLimits(options = {}) {
56
+ const source = options && typeof options === "object" ? options : {};
57
+ const maxBytes = clampInteger(source.maxBytes, DEFAULT_MAX_BYTES, 0, 2 * 1024 * MIB);
58
+ const maxEntries = clampInteger(source.maxEntries, DEFAULT_MAX_ENTRIES, 0, 4_096);
59
+ const defaultItemBytes = Math.min(DEFAULT_MAX_ITEM_BYTES, maxBytes);
60
+ const configuredItemBytes = source.maxItemBytes ?? source.maxEntryBytes;
61
+ const maxItemBytes = maxBytes === 0
62
+ ? 0
63
+ : clampInteger(configuredItemBytes, defaultItemBytes, 0, maxBytes);
64
+ return Object.freeze({ maxBytes, maxEntries, maxItemBytes });
65
+ }
66
+
67
+ /**
68
+ * Pure cache-key normalization. Invalid or potentially collision-prone inputs
69
+ * return null instead of being truncated.
70
+ */
71
+ function normalizeFrameCacheKey(input, pageIndex, variant = "") {
72
+ const source = input && typeof input === "object"
73
+ ? input
74
+ : { documentId: input, pageIndex, variant };
75
+
76
+ const documentId = normalizeBoundedString(source.documentId, MAX_DOCUMENT_ID_LENGTH, { trim: true });
77
+ let normalizedPageIndex;
78
+ let normalizedVariant;
79
+ try {
80
+ normalizedPageIndex = Number(source.pageIndex);
81
+ normalizedVariant = source.variant == null ? "" : String(source.variant);
82
+ } catch {
83
+ return null;
84
+ }
85
+
86
+ if (
87
+ documentId == null
88
+ || !Number.isSafeInteger(normalizedPageIndex)
89
+ || normalizedPageIndex < 0
90
+ || normalizedPageIndex > MAX_PAGE_INDEX
91
+ || normalizedVariant.length > MAX_VARIANT_LENGTH
92
+ ) {
93
+ return null;
94
+ }
95
+
96
+ // Length-prefixing prevents separators inside opaque identifiers from
97
+ // creating collisions.
98
+ const key = `v1:${documentId.length}:${documentId}:${normalizedPageIndex}:${normalizedVariant.length}:${normalizedVariant}`;
99
+ return Object.freeze({
100
+ key,
101
+ documentId,
102
+ pageIndex: normalizedPageIndex,
103
+ variant: normalizedVariant,
104
+ });
105
+ }
106
+
107
+ function createFrameCacheKey(documentId, pageIndex, variant = "") {
108
+ return normalizeFrameCacheKey(documentId, pageIndex, variant)?.key ?? null;
109
+ }
110
+
111
+ function normalizeDatabaseName(value) {
112
+ return normalizeBoundedString(value, MAX_DATABASE_NAME_LENGTH, { trim: true }) ?? DEFAULT_DB_NAME;
113
+ }
114
+
115
+ function isPngBlob(blob) {
116
+ return Boolean(
117
+ blob
118
+ && typeof blob === "object"
119
+ && Number.isSafeInteger(blob.size)
120
+ && blob.size > 0
121
+ && typeof blob.arrayBuffer === "function"
122
+ && typeof blob.type === "string"
123
+ && blob.type.toLowerCase() === "image/png",
124
+ );
125
+ }
126
+
127
+ function normalizeDimension(value) {
128
+ let numeric;
129
+ try {
130
+ numeric = Number(value);
131
+ } catch {
132
+ return null;
133
+ }
134
+ return Number.isSafeInteger(numeric) && numeric > 0 && numeric <= MAX_DIMENSION
135
+ ? numeric
136
+ : null;
137
+ }
138
+
139
+ function normalizePutArguments(keyInput, frameInput, limits) {
140
+ const frame = frameInput && typeof frameInput === "object" ? frameInput : keyInput;
141
+ const key = normalizeFrameCacheKey(keyInput);
142
+ const width = normalizeDimension(frame?.width);
143
+ const height = normalizeDimension(frame?.height);
144
+ const blob = frame?.blob;
145
+
146
+ if (key == null || width == null || height == null || !isPngBlob(blob)) {
147
+ return null;
148
+ }
149
+ if (blob.size > limits.maxItemBytes || blob.size > limits.maxBytes) {
150
+ return null;
151
+ }
152
+ return { ...key, blob, width, height, byteSize: blob.size };
153
+ }
154
+
155
+ function requestResult(request) {
156
+ return new Promise((resolve, reject) => {
157
+ request.onsuccess = () => resolve(request.result);
158
+ request.onerror = () => reject(request.error ?? new Error("IndexedDB request failed"));
159
+ });
160
+ }
161
+
162
+ function transactionError(transaction, fallbackMessage) {
163
+ return transaction.error ?? new Error(fallbackMessage);
164
+ }
165
+
166
+ function openDatabase(factory, dbName) {
167
+ return new Promise((resolve, reject) => {
168
+ let settled = false;
169
+ let request;
170
+ let timer = null;
171
+
172
+ const settle = (callback, value) => {
173
+ if (settled) {
174
+ if (value && typeof value.close === "function") {
175
+ value.close();
176
+ }
177
+ return;
178
+ }
179
+ settled = true;
180
+ if (timer != null) {
181
+ clearTimeout(timer);
182
+ }
183
+ callback(value);
184
+ };
185
+
186
+ try {
187
+ request = factory.open(dbName, DB_VERSION);
188
+ } catch (error) {
189
+ settle(reject, error);
190
+ return;
191
+ }
192
+
193
+ request.onupgradeneeded = () => {
194
+ const database = request.result;
195
+ let frameStore;
196
+ if (!database.objectStoreNames.contains(FRAME_STORE)) {
197
+ frameStore = database.createObjectStore(FRAME_STORE, { keyPath: "key" });
198
+ } else {
199
+ frameStore = request.transaction.objectStore(FRAME_STORE);
200
+ }
201
+ if (!frameStore.indexNames.contains(DOCUMENT_INDEX)) {
202
+ frameStore.createIndex(DOCUMENT_INDEX, "documentId", { unique: false });
203
+ }
204
+ if (!frameStore.indexNames.contains(ACCESS_INDEX)) {
205
+ frameStore.createIndex(ACCESS_INDEX, "lastAccess", { unique: false });
206
+ }
207
+ if (!database.objectStoreNames.contains(META_STORE)) {
208
+ database.createObjectStore(META_STORE, { keyPath: "key" });
209
+ }
210
+ };
211
+ request.onsuccess = () => settle(resolve, request.result);
212
+ request.onerror = () => settle(reject, request.error ?? new Error("Unable to open IndexedDB"));
213
+ request.onblocked = () => settle(reject, new Error("IndexedDB open was blocked"));
214
+
215
+ if (typeof setTimeout === "function") {
216
+ timer = setTimeout(() => {
217
+ settle(reject, new Error("IndexedDB open timed out"));
218
+ }, OPEN_TIMEOUT_MS);
219
+ }
220
+ });
221
+ }
222
+
223
+ function isStoredFrameValid(record) {
224
+ return Boolean(
225
+ record
226
+ && typeof record === "object"
227
+ && typeof record.key === "string"
228
+ && typeof record.documentId === "string"
229
+ && Number.isSafeInteger(record.pageIndex)
230
+ && normalizeDimension(record.width) != null
231
+ && normalizeDimension(record.height) != null
232
+ && Number.isSafeInteger(record.byteSize)
233
+ && record.byteSize > 0
234
+ && isPngBlob(record.blob)
235
+ && record.blob.size === record.byteSize,
236
+ );
237
+ }
238
+
239
+ function reconcileUsage(database, limits) {
240
+ return new Promise((resolve, reject) => {
241
+ let transaction;
242
+ try {
243
+ transaction = database.transaction([FRAME_STORE, META_STORE], "readwrite");
244
+ } catch (error) {
245
+ reject(error);
246
+ return;
247
+ }
248
+
249
+ const frames = transaction.objectStore(FRAME_STORE);
250
+ const meta = transaction.objectStore(META_STORE);
251
+ let totalBytes = 0;
252
+ let totalEntries = 0;
253
+
254
+ const writeUsage = () => {
255
+ meta.put({
256
+ key: USAGE_KEY,
257
+ totalBytes,
258
+ totalEntries,
259
+ updatedAt: Date.now(),
260
+ });
261
+ };
262
+
263
+ const withinLimits = () => (
264
+ totalBytes <= limits.maxBytes
265
+ && totalEntries <= limits.maxEntries
266
+ );
267
+
268
+ const trimOldest = () => {
269
+ if (withinLimits()) {
270
+ writeUsage();
271
+ return;
272
+ }
273
+ const trimRequest = frames.index(ACCESS_INDEX).openCursor();
274
+ trimRequest.onsuccess = () => {
275
+ const cursor = trimRequest.result;
276
+ if (cursor == null) {
277
+ totalBytes = 0;
278
+ totalEntries = 0;
279
+ writeUsage();
280
+ return;
281
+ }
282
+ const record = cursor.value;
283
+ cursor.delete();
284
+ if (isStoredFrameValid(record)) {
285
+ totalBytes = Math.max(0, totalBytes - record.byteSize);
286
+ totalEntries = Math.max(0, totalEntries - 1);
287
+ }
288
+ if (withinLimits()) {
289
+ writeUsage();
290
+ } else {
291
+ cursor.continue();
292
+ }
293
+ };
294
+ };
295
+
296
+ const cursorRequest = frames.openCursor();
297
+ cursorRequest.onsuccess = () => {
298
+ const cursor = cursorRequest.result;
299
+ if (cursor == null) {
300
+ trimOldest();
301
+ return;
302
+ }
303
+ const record = cursor.value;
304
+ if (isStoredFrameValid(record) && record.byteSize <= limits.maxItemBytes) {
305
+ totalBytes += record.byteSize;
306
+ totalEntries += 1;
307
+ } else {
308
+ cursor.delete();
309
+ }
310
+ cursor.continue();
311
+ };
312
+
313
+ transaction.oncomplete = () => resolve({ totalBytes, totalEntries });
314
+ transaction.onerror = () => reject(transactionError(transaction, "IndexedDB reconciliation failed"));
315
+ transaction.onabort = () => reject(transactionError(transaction, "IndexedDB reconciliation was aborted"));
316
+ });
317
+ }
318
+
319
+ function readUsageRecord(value) {
320
+ const totalBytes = Number.isSafeInteger(value?.totalBytes) && value.totalBytes >= 0
321
+ ? value.totalBytes
322
+ : 0;
323
+ const totalEntries = Number.isSafeInteger(value?.totalEntries) && value.totalEntries >= 0
324
+ ? value.totalEntries
325
+ : 0;
326
+ return { totalBytes, totalEntries };
327
+ }
328
+
329
+ function putFrame(database, record, limits) {
330
+ return new Promise((resolve, reject) => {
331
+ let transaction;
332
+ try {
333
+ transaction = database.transaction([FRAME_STORE, META_STORE], "readwrite");
334
+ } catch (error) {
335
+ reject(error);
336
+ return;
337
+ }
338
+
339
+ const frames = transaction.objectStore(FRAME_STORE);
340
+ const meta = transaction.objectStore(META_STORE);
341
+ let existing;
342
+ let usage;
343
+ let existingReady = false;
344
+ let usageReady = false;
345
+ let started = false;
346
+
347
+ const failRequest = () => {
348
+ try {
349
+ transaction.abort();
350
+ } catch {
351
+ // The transaction may already have been aborted by the browser.
352
+ }
353
+ };
354
+
355
+ const finalizeWrite = () => {
356
+ const now = record.lastAccess;
357
+ frames.put(record);
358
+ meta.put({
359
+ key: USAGE_KEY,
360
+ totalBytes: usage.totalBytes,
361
+ totalEntries: usage.totalEntries,
362
+ updatedAt: now,
363
+ });
364
+ };
365
+
366
+ const evictIfNeeded = () => {
367
+ if (started || !existingReady || !usageReady) {
368
+ return;
369
+ }
370
+ started = true;
371
+
372
+ const oldBytes = isStoredFrameValid(existing) ? existing.byteSize : 0;
373
+ const oldEntries = isStoredFrameValid(existing) ? 1 : 0;
374
+ usage.totalBytes = Math.max(0, usage.totalBytes - oldBytes) + record.byteSize;
375
+ usage.totalEntries = Math.max(0, usage.totalEntries - oldEntries) + 1;
376
+
377
+ const withinLimits = () => (
378
+ usage.totalBytes <= limits.maxBytes
379
+ && usage.totalEntries <= limits.maxEntries
380
+ );
381
+
382
+ if (withinLimits()) {
383
+ finalizeWrite();
384
+ return;
385
+ }
386
+
387
+ const cursorRequest = frames.index(ACCESS_INDEX).openCursor();
388
+ cursorRequest.onsuccess = () => {
389
+ const cursor = cursorRequest.result;
390
+ if (cursor == null) {
391
+ if (withinLimits()) {
392
+ finalizeWrite();
393
+ } else {
394
+ failRequest();
395
+ }
396
+ return;
397
+ }
398
+
399
+ const candidate = cursor.value;
400
+ if (candidate?.key !== record.key && isStoredFrameValid(candidate)) {
401
+ cursor.delete();
402
+ usage.totalBytes = Math.max(0, usage.totalBytes - candidate.byteSize);
403
+ usage.totalEntries = Math.max(0, usage.totalEntries - 1);
404
+ } else if (candidate?.key !== record.key) {
405
+ cursor.delete();
406
+ }
407
+
408
+ if (withinLimits()) {
409
+ finalizeWrite();
410
+ } else {
411
+ cursor.continue();
412
+ }
413
+ };
414
+ cursorRequest.onerror = failRequest;
415
+ };
416
+
417
+ const existingRequest = frames.get(record.key);
418
+ existingRequest.onsuccess = () => {
419
+ existing = existingRequest.result;
420
+ existingReady = true;
421
+ evictIfNeeded();
422
+ };
423
+ existingRequest.onerror = failRequest;
424
+
425
+ const usageRequest = meta.get(USAGE_KEY);
426
+ usageRequest.onsuccess = () => {
427
+ usage = readUsageRecord(usageRequest.result);
428
+ usageReady = true;
429
+ evictIfNeeded();
430
+ };
431
+ usageRequest.onerror = failRequest;
432
+
433
+ transaction.oncomplete = () => resolve(true);
434
+ transaction.onerror = () => reject(transactionError(transaction, "IndexedDB frame write failed"));
435
+ transaction.onabort = () => reject(transactionError(transaction, "IndexedDB frame write was aborted"));
436
+ });
437
+ }
438
+
439
+ function getFrame(database, key, nextAccess) {
440
+ return new Promise((resolve, reject) => {
441
+ let transaction;
442
+ try {
443
+ transaction = database.transaction(FRAME_STORE, "readwrite");
444
+ } catch (error) {
445
+ reject(error);
446
+ return;
447
+ }
448
+
449
+ const store = transaction.objectStore(FRAME_STORE);
450
+ let result = null;
451
+ const request = store.get(key.key);
452
+ request.onsuccess = () => {
453
+ const record = request.result;
454
+ if (!isStoredFrameValid(record)) {
455
+ if (record != null) {
456
+ store.delete(key.key);
457
+ }
458
+ return;
459
+ }
460
+ record.lastAccess = nextAccess();
461
+ store.put(record);
462
+ result = {
463
+ blob: record.blob,
464
+ width: record.width,
465
+ height: record.height,
466
+ byteSize: record.byteSize,
467
+ lastAccess: record.lastAccess,
468
+ };
469
+ };
470
+
471
+ transaction.oncomplete = () => resolve(result);
472
+ transaction.onerror = () => reject(transactionError(transaction, "IndexedDB frame read failed"));
473
+ transaction.onabort = () => reject(transactionError(transaction, "IndexedDB frame read was aborted"));
474
+ });
475
+ }
476
+
477
+ function deleteFrame(database, key) {
478
+ return new Promise((resolve, reject) => {
479
+ let transaction;
480
+ try {
481
+ transaction = database.transaction([FRAME_STORE, META_STORE], "readwrite");
482
+ } catch (error) {
483
+ reject(error);
484
+ return;
485
+ }
486
+
487
+ const frames = transaction.objectStore(FRAME_STORE);
488
+ const meta = transaction.objectStore(META_STORE);
489
+ let deleted = false;
490
+ const getRequest = frames.get(key.key);
491
+ getRequest.onsuccess = () => {
492
+ const existing = getRequest.result;
493
+ if (existing == null) {
494
+ return;
495
+ }
496
+ deleted = true;
497
+ const existingIsValid = isStoredFrameValid(existing);
498
+ const usageRequest = meta.get(USAGE_KEY);
499
+ usageRequest.onsuccess = () => {
500
+ const usage = readUsageRecord(usageRequest.result);
501
+ frames.delete(key.key);
502
+ meta.put({
503
+ key: USAGE_KEY,
504
+ totalBytes: Math.max(0, usage.totalBytes - (existingIsValid ? existing.byteSize : 0)),
505
+ totalEntries: Math.max(0, usage.totalEntries - (existingIsValid ? 1 : 0)),
506
+ updatedAt: Date.now(),
507
+ });
508
+ };
509
+ };
510
+
511
+ transaction.oncomplete = () => resolve(deleted);
512
+ transaction.onerror = () => reject(transactionError(transaction, "IndexedDB frame delete failed"));
513
+ transaction.onabort = () => reject(transactionError(transaction, "IndexedDB frame delete was aborted"));
514
+ });
515
+ }
516
+
517
+ function clearDocumentFrames(database, documentId) {
518
+ return new Promise((resolve, reject) => {
519
+ let transaction;
520
+ try {
521
+ transaction = database.transaction([FRAME_STORE, META_STORE], "readwrite");
522
+ } catch (error) {
523
+ reject(error);
524
+ return;
525
+ }
526
+
527
+ const frames = transaction.objectStore(FRAME_STORE);
528
+ const meta = transaction.objectStore(META_STORE);
529
+ let usage = null;
530
+ let cursorFinished = false;
531
+ let deletedEntries = 0;
532
+ let deletedBytes = 0;
533
+ let finalized = false;
534
+
535
+ const maybeFinalize = () => {
536
+ if (finalized || usage == null || !cursorFinished) {
537
+ return;
538
+ }
539
+ finalized = true;
540
+ meta.put({
541
+ key: USAGE_KEY,
542
+ totalBytes: Math.max(0, usage.totalBytes - deletedBytes),
543
+ totalEntries: Math.max(0, usage.totalEntries - deletedEntries),
544
+ updatedAt: Date.now(),
545
+ });
546
+ };
547
+
548
+ const usageRequest = meta.get(USAGE_KEY);
549
+ usageRequest.onsuccess = () => {
550
+ usage = readUsageRecord(usageRequest.result);
551
+ maybeFinalize();
552
+ };
553
+
554
+ const cursorRequest = frames.index(DOCUMENT_INDEX).openCursor(documentId);
555
+ cursorRequest.onsuccess = () => {
556
+ const cursor = cursorRequest.result;
557
+ if (cursor == null) {
558
+ cursorFinished = true;
559
+ maybeFinalize();
560
+ return;
561
+ }
562
+ const record = cursor.value;
563
+ if (isStoredFrameValid(record)) {
564
+ deletedEntries += 1;
565
+ deletedBytes += record.byteSize;
566
+ }
567
+ cursor.delete();
568
+ cursor.continue();
569
+ };
570
+
571
+ transaction.oncomplete = () => resolve(deletedEntries);
572
+ transaction.onerror = () => reject(transactionError(transaction, "IndexedDB document clear failed"));
573
+ transaction.onabort = () => reject(transactionError(transaction, "IndexedDB document clear was aborted"));
574
+ });
575
+ }
576
+
577
+ /**
578
+ * Create a cache facade that is safe in Window and DedicatedWorkerGlobalScope.
579
+ * The facade is synchronous; all storage methods are asynchronous and never
580
+ * throw for storage availability, quota, cloning, or transaction failures.
581
+ */
582
+ function createFrameCache(options = {}) {
583
+ const source = options && typeof options === "object" ? options : {};
584
+ const limits = normalizeFrameCacheLimits(source);
585
+ const dbName = normalizeDatabaseName(source.dbName);
586
+ const factory = source.indexedDB ?? globalThis.indexedDB;
587
+ const enabled = source.enabled !== false;
588
+ let closed = false;
589
+ let disabledReason = !enabled
590
+ ? "disabled"
591
+ : factory && typeof factory.open === "function"
592
+ ? null
593
+ : "unavailable";
594
+ let databasePromise = null;
595
+ let lastAccess = 0;
596
+
597
+ const nextAccess = () => {
598
+ lastAccess = Math.max(Date.now(), lastAccess + 1);
599
+ return lastAccess;
600
+ };
601
+
602
+ const disable = (error) => {
603
+ if (disabledReason == null) {
604
+ disabledReason = typeof error?.name === "string" && error.name
605
+ ? error.name
606
+ : "storage-error";
607
+ }
608
+ if (databasePromise != null) {
609
+ databasePromise.then((database) => database?.close()).catch(() => {});
610
+ }
611
+ };
612
+
613
+ const getDatabase = async () => {
614
+ if (closed || disabledReason != null) {
615
+ return null;
616
+ }
617
+ if (databasePromise == null) {
618
+ databasePromise = openDatabase(factory, dbName)
619
+ .then(async (database) => {
620
+ database.onversionchange = () => {
621
+ database.close();
622
+ disable({ name: "version-change" });
623
+ };
624
+ await reconcileUsage(database, limits);
625
+ return database;
626
+ })
627
+ .catch((error) => {
628
+ disable(error);
629
+ return null;
630
+ });
631
+ }
632
+ return databasePromise;
633
+ };
634
+
635
+ const run = async (operation, fallback) => {
636
+ if (closed || disabledReason != null) {
637
+ return fallback;
638
+ }
639
+ const database = await getDatabase();
640
+ if (database == null) {
641
+ return fallback;
642
+ }
643
+ try {
644
+ return await operation(database);
645
+ } catch (error) {
646
+ // QuotaExceededError, Safari's InvalidStateError / TransactionInactiveError,
647
+ // private-mode failures, and structured-clone failures all become misses.
648
+ disable(error);
649
+ return fallback;
650
+ }
651
+ };
652
+
653
+ return Object.freeze({
654
+ async get(keyInput) {
655
+ const key = normalizeFrameCacheKey(keyInput);
656
+ if (key == null) {
657
+ return null;
658
+ }
659
+ return run((database) => getFrame(database, key, nextAccess), null);
660
+ },
661
+
662
+ async put(keyInput, frameInput) {
663
+ const input = normalizePutArguments(keyInput, frameInput, limits);
664
+ if (input == null) {
665
+ return false;
666
+ }
667
+ const record = {
668
+ ...input,
669
+ lastAccess: nextAccess(),
670
+ };
671
+ return run((database) => putFrame(database, record, limits), false);
672
+ },
673
+
674
+ async delete(keyInput) {
675
+ const key = normalizeFrameCacheKey(keyInput);
676
+ if (key == null) {
677
+ return false;
678
+ }
679
+ return run((database) => deleteFrame(database, key), false);
680
+ },
681
+
682
+ async clearDocument(documentId) {
683
+ const normalized = normalizeBoundedString(documentId, MAX_DOCUMENT_ID_LENGTH, { trim: true });
684
+ if (normalized == null) {
685
+ return 0;
686
+ }
687
+ return run((database) => clearDocumentFrames(database, normalized), 0);
688
+ },
689
+
690
+ async stats() {
691
+ const unavailable = () => ({
692
+ available: false,
693
+ disabledReason: closed ? "closed" : disabledReason ?? "unavailable",
694
+ entries: 0,
695
+ totalBytes: 0,
696
+ ...limits,
697
+ });
698
+ if (closed || disabledReason != null) {
699
+ return unavailable();
700
+ }
701
+ const result = await run(async (database) => {
702
+ let transaction;
703
+ try {
704
+ transaction = database.transaction(META_STORE, "readonly");
705
+ } catch (error) {
706
+ throw error;
707
+ }
708
+ const value = await requestResult(transaction.objectStore(META_STORE).get(USAGE_KEY));
709
+ const usage = readUsageRecord(value);
710
+ return {
711
+ available: true,
712
+ disabledReason: null,
713
+ entries: usage.totalEntries,
714
+ totalBytes: usage.totalBytes,
715
+ ...limits,
716
+ };
717
+ }, null);
718
+ return result ?? unavailable();
719
+ },
720
+
721
+ close() {
722
+ if (closed) {
723
+ return;
724
+ }
725
+ closed = true;
726
+ if (databasePromise != null) {
727
+ databasePromise.then((database) => database?.close()).catch(() => {});
728
+ }
729
+ },
730
+ });
731
+ }
2
732
 
3
733
  const WASM_EXPORTS = Object.freeze([
4
734
  'memory',