frond-js 0.4.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 +21 -0
- package/README.en.md +247 -0
- package/README.md +211 -0
- package/dist/abort-BY8vBk0v.d.cts +99 -0
- package/dist/abort-BY8vBk0v.d.ts +99 -0
- package/dist/adapter-3G46J3CA.cjs +503 -0
- package/dist/adapter-3ONQWJVQ.js +501 -0
- package/dist/adapter-55QHWRSE.js +124 -0
- package/dist/adapter-DF34GBWJ.cjs +19 -0
- package/dist/adapter-EXTNILTC.cjs +126 -0
- package/dist/adapter-GOFC7TMC.js +284 -0
- package/dist/adapter-LRJTQ47I.cjs +286 -0
- package/dist/adapter-TWWZML4A.js +17 -0
- package/dist/adapter-ZM5FQTJT.js +1415 -0
- package/dist/adapter-ZRNSDQUV.cjs +1417 -0
- package/dist/chunk-2SUG7YFZ.cjs +108 -0
- package/dist/chunk-B2L2YVXD.js +89 -0
- package/dist/chunk-D4KWSEZD.js +393 -0
- package/dist/chunk-EZTIZO6R.cjs +430 -0
- package/dist/chunk-G7DLWGBW.cjs +103 -0
- package/dist/chunk-GTGLDLJD.cjs +479 -0
- package/dist/chunk-IIV6VIUJ.cjs +83 -0
- package/dist/chunk-JDTHZQUK.js +102 -0
- package/dist/chunk-JJVXT3AC.js +30 -0
- package/dist/chunk-LIXRYQL2.js +473 -0
- package/dist/chunk-NABYHI6X.cjs +400 -0
- package/dist/chunk-SJVOYNTF.js +425 -0
- package/dist/chunk-SLI2YL25.cjs +252 -0
- package/dist/chunk-U4264IQH.js +78 -0
- package/dist/chunk-UY2YRCFC.js +250 -0
- package/dist/chunk-WCZTTQ7Z.cjs +32 -0
- package/dist/core/index.cjs +162 -0
- package/dist/core/index.d.cts +321 -0
- package/dist/core/index.d.ts +321 -0
- package/dist/core/index.js +49 -0
- package/dist/default-DRLIJX73.js +1183 -0
- package/dist/default-UK52WOO5.cjs +1192 -0
- package/dist/formats/epub/index.cjs +29 -0
- package/dist/formats/epub/index.d.cts +286 -0
- package/dist/formats/epub/index.d.ts +286 -0
- package/dist/formats/epub/index.js +11 -0
- package/dist/index.cjs +655 -0
- package/dist/index.d.cts +335 -0
- package/dist/index.d.ts +335 -0
- package/dist/index.js +600 -0
- package/dist/render/index.cjs +2 -0
- package/dist/render/index.d.cts +116 -0
- package/dist/render/index.d.ts +116 -0
- package/dist/render/index.js +1 -0
- package/dist/types-B76GOMxj.d.ts +129 -0
- package/dist/types-B7mslPBY.d.cts +166 -0
- package/dist/types-B7mslPBY.d.ts +166 -0
- package/dist/types-BH88rUYt.d.cts +129 -0
- package/dist/types-C-5eHSRH.d.ts +379 -0
- package/dist/types-CPUqTEPW.d.cts +379 -0
- package/dist/types-DQYmArgv.d.cts +17 -0
- package/dist/types-DQYmArgv.d.ts +17 -0
- package/package.json +115 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkGTGLDLJD_cjs = require('./chunk-GTGLDLJD.cjs');
|
|
4
|
+
var chunkWCZTTQ7Z_cjs = require('./chunk-WCZTTQ7Z.cjs');
|
|
5
|
+
var chunk2SUG7YFZ_cjs = require('./chunk-2SUG7YFZ.cjs');
|
|
6
|
+
var chunkSLI2YL25_cjs = require('./chunk-SLI2YL25.cjs');
|
|
7
|
+
require('./chunk-IIV6VIUJ.cjs');
|
|
8
|
+
var chunkG7DLWGBW_cjs = require('./chunk-G7DLWGBW.cjs');
|
|
9
|
+
|
|
10
|
+
// src/core/formats/registry.ts
|
|
11
|
+
function createFormatRegistry(loaders) {
|
|
12
|
+
return {
|
|
13
|
+
async open(source, options = {}) {
|
|
14
|
+
chunkG7DLWGBW_cjs.throwIfAborted(options.signal);
|
|
15
|
+
const bytes = await chunkWCZTTQ7Z_cjs.resolveBookBytes(source, options);
|
|
16
|
+
const label = options.source ?? "<anonymous>";
|
|
17
|
+
const outcome = chunk2SUG7YFZ_cjs.probeFormat(bytes.subarray(0, chunk2SUG7YFZ_cjs.PROBE_PREFIX_BYTES));
|
|
18
|
+
if (outcome === null) {
|
|
19
|
+
throw new chunkG7DLWGBW_cjs.FormatError(
|
|
20
|
+
`\u65E0\u6CD5\u8BC6\u522B\u7684\u4E66\u7C4D\u683C\u5F0F\uFF1A\u524D ${String(chunk2SUG7YFZ_cjs.PROBE_PREFIX_BYTES)} \u5B57\u8282\u4E0D\u5339\u914D\u4EFB\u4F55\u5DF2\u77E5\u5224\u636E\uFF08\u6E90: ${label}\uFF09`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
const hit = outcome.kind === "format" ? outcome : probeContainer(bytes, options, label);
|
|
24
|
+
const loader = loaders[hit.format];
|
|
25
|
+
if (loader === void 0) {
|
|
26
|
+
throw new chunkG7DLWGBW_cjs.FormatError(
|
|
27
|
+
`\u8BC6\u522B\u4E3A ${hit.format}\uFF08\u5224\u636E\uFF1A${hit.evidence}\uFF09\uFF0C\u4F46\u8BE5\u683C\u5F0F\u7684\u9002\u914D\u5668\u5C1A\u672A\u5B9E\u73B0\uFF08\u6E90: ${label}\uFF09`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
const adapter = await loader();
|
|
31
|
+
chunkG7DLWGBW_cjs.throwIfAborted(options.signal);
|
|
32
|
+
return adapter.open(bytes, options);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function probeContainer(bytes, options, label) {
|
|
37
|
+
const archive = chunkSLI2YL25_cjs.parseZip(bytes, options);
|
|
38
|
+
const hit = chunk2SUG7YFZ_cjs.probeZipContainer(archive.entries.map((entry) => entry.name));
|
|
39
|
+
if (hit === null) {
|
|
40
|
+
throw new chunkG7DLWGBW_cjs.FormatError(
|
|
41
|
+
`ZIP \u5BB9\u5668\u65E2\u65E0 ${chunk2SUG7YFZ_cjs.EPUB_CONTAINER_ENTRY}\uFF0C\u4E5F\u65E0\u56FE\u7247 / .fb2 \u6761\u76EE\uFF0C\u65E0\u6CD5\u5224\u5B9A\u683C\u5F0F\uFF08\u6E90: ${label}\uFF09`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
return hit;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/reader/state.ts
|
|
48
|
+
var READER_STATES = [
|
|
49
|
+
"idle",
|
|
50
|
+
"loading",
|
|
51
|
+
"ready",
|
|
52
|
+
"paginating",
|
|
53
|
+
"error",
|
|
54
|
+
"closed"
|
|
55
|
+
];
|
|
56
|
+
var TRANSITIONS = {
|
|
57
|
+
idle: ["loading", "closed"],
|
|
58
|
+
loading: ["ready", "error", "closed"],
|
|
59
|
+
ready: ["paginating", "closed"],
|
|
60
|
+
paginating: ["ready", "error", "closed"],
|
|
61
|
+
error: ["loading", "closed"],
|
|
62
|
+
closed: []
|
|
63
|
+
};
|
|
64
|
+
function canTransition(from, to) {
|
|
65
|
+
return TRANSITIONS[from].includes(to);
|
|
66
|
+
}
|
|
67
|
+
var ReaderStateMachine = class {
|
|
68
|
+
#state = "idle";
|
|
69
|
+
#listeners = /* @__PURE__ */ new Set();
|
|
70
|
+
/** 当前状态。 */
|
|
71
|
+
get state() {
|
|
72
|
+
return this.#state;
|
|
73
|
+
}
|
|
74
|
+
/** 是否已进入终态。 */
|
|
75
|
+
get isTerminal() {
|
|
76
|
+
return this.#state === "closed";
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 执行一次状态迁移。
|
|
80
|
+
*
|
|
81
|
+
* @throws {StateError} 迁移不在 {@link TRANSITIONS} 中时
|
|
82
|
+
*/
|
|
83
|
+
transition(to) {
|
|
84
|
+
const from = this.#state;
|
|
85
|
+
if (!canTransition(from, to)) {
|
|
86
|
+
throw new chunkG7DLWGBW_cjs.StateError(`\u975E\u6CD5\u72B6\u6001\u8FC1\u79FB\uFF1A${from} \u2192 ${to}`);
|
|
87
|
+
}
|
|
88
|
+
this.#state = to;
|
|
89
|
+
this.#emit(to);
|
|
90
|
+
return to;
|
|
91
|
+
}
|
|
92
|
+
/** 订阅状态变化,返回取消订阅函数(幂等)。 */
|
|
93
|
+
subscribe(listener) {
|
|
94
|
+
this.#listeners.add(listener);
|
|
95
|
+
return () => {
|
|
96
|
+
this.#listeners.delete(listener);
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
#emit(state) {
|
|
100
|
+
for (const listener of [...this.#listeners]) {
|
|
101
|
+
listener(state);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// src/reader/formats.ts
|
|
107
|
+
var DEFAULT_FORMAT_LOADERS = {
|
|
108
|
+
epub: async () => (await import('./adapter-DF34GBWJ.cjs')).epubAdapter,
|
|
109
|
+
// MOBI(T3.2a):与 EPUB 同一套规矩 —— 深路径进 `adapter.js`(**不是** subpath
|
|
110
|
+
// 入口),本批**不**为 MOBI 新开 `frond-js/formats/mobi` 公开入口。理由与代价
|
|
111
|
+
// 见上面那段(0.24 KB gzip 换「不发布一个消费者无法命名其类型的公共 API」)。
|
|
112
|
+
//
|
|
113
|
+
// ⚠️ 探测侧**不需要**改:`core/formats/probe.ts` 早已按 PalmDB 头 offset 60 的
|
|
114
|
+
// `BOOKMOBI` 标记判出 `'mobi'`(T3.0.3 就绪,本批只是把 thunk 补上)。
|
|
115
|
+
// 也就是说「认得出但没实现」这条路径**被本行关掉了** —— 在此之前,一个 MOBI
|
|
116
|
+
// 文件会走到注册表的「尚未实现」分支;现在它会真的被打开。
|
|
117
|
+
mobi: async () => (await import('./adapter-ZRNSDQUV.cjs')).mobiAdapter,
|
|
118
|
+
// FB2(T3.3):与前两者同一套规矩 —— 深路径进 `adapter.js`(**不是** subpath 入口),
|
|
119
|
+
// 本批**不**为 FB2 新开 `frond-js/formats/fb2` 公开入口。理由与代价见上面那段。
|
|
120
|
+
//
|
|
121
|
+
// ⚠️ 探测侧**不需要**改:`core/formats/probe.ts` 早已就绪两级判据 ——
|
|
122
|
+
// 一级 `looksLikeFb2`(BOM / 前置空白容忍 + 前缀含 `<FictionBook`),
|
|
123
|
+
// 二级 `probeZipContainer` 的 `.fb2.zip`(ZIP 内含 `.fb2` 且无 `container.xml`)。
|
|
124
|
+
// 也就是说「认得出但没实现」这条路径**被本行关掉了**。
|
|
125
|
+
fb2: async () => (await import('./adapter-3G46J3CA.cjs')).fb2Adapter,
|
|
126
|
+
// CBZ(T3.4):与前四者同一套规矩 —— 深路径进 `adapter.js`(**不是** subpath 入口),
|
|
127
|
+
// 本批**不**为 CBZ 新开 `frond-js/formats/cbz` 公开入口。理由与代价见上面那段。
|
|
128
|
+
//
|
|
129
|
+
// ⚠️ 探测侧**不需要**改:`core/formats/probe.ts` 的 `probeZipContainer` 早已就绪
|
|
130
|
+
// 第三条判据(ZIP 内含 `IMAGE_EXTENSIONS` 里的扩展名,且无 `container.xml`、无 `.fb2`)。
|
|
131
|
+
// 也就是说「认得出但没实现」这条路径**被本行关掉了**。
|
|
132
|
+
//
|
|
133
|
+
// ⚠️ **与 FB2 / MOBI 的差别**:CBZ **没有裸文件形态**(它必是 ZIP)⇒ 只走**二级**探测
|
|
134
|
+
// 那一条入口,`probeFormat`(一级、按魔数)永远判不出它。故 `cbz-adapter.test.ts`
|
|
135
|
+
// 比 `fb2-adapter.test.ts` 少一组用例 —— 少的那组不是漏了,是**不存在**。
|
|
136
|
+
cbz: async () => (await import('./adapter-EXTNILTC.cjs')).cbzAdapter,
|
|
137
|
+
// PDF(T3.5):与前五者同一套规矩 —— 深路径进 `adapter.js`(**不是** subpath 入口),
|
|
138
|
+
// 本批**不**为 PDF 新开 `frond-js/formats/pdf` 公开入口。理由与代价见上面那段。
|
|
139
|
+
//
|
|
140
|
+
// ⚠️ 探测侧**不需要**改:`core/formats/probe.ts` 的 `probeFormat` 早已就绪
|
|
141
|
+
// **第一条判据**(`%PDF-` 魔数 @ offset 0,**一级**、不需要容器)。也就是说
|
|
142
|
+
// 「认得出但没实现」这条路径**被本行关掉了**。
|
|
143
|
+
//
|
|
144
|
+
// ⚠️ **与 CBZ / FB2 / MOBI 的差别**:PDF 是**单文件**,探测走一级判据
|
|
145
|
+
// ⇒ `pdf-adapter.test.ts` 里那组「容器内条目名」的用例**不存在**(不是漏了)。
|
|
146
|
+
//
|
|
147
|
+
// ⚠️ 本行是「格式包不进根入口闭包」在 PDF 上的落点:`adapter.js` 里那个字面量
|
|
148
|
+
// `import('pdfjs-dist/legacy/build/pdf.mjs')` 是本包对 PDF.js 的**唯一引用**,
|
|
149
|
+
// `tsup` 按 `peerDependencies` 把它判为 external ⇒ PDF.js **不进 `dist/`**。
|
|
150
|
+
// 这条机制已由受控对照确证(移除 peer 声明 ⇒ 836.17 KB 被内联),且**接线后复测过**。
|
|
151
|
+
pdf: async () => (await import('./adapter-LRJTQ47I.cjs')).pdfAdapter
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// src/reader/render-layer.ts
|
|
155
|
+
var DEFAULT_RENDER_LOADERS = {
|
|
156
|
+
default: async () => (await import('./default-UK52WOO5.cjs')).createDefaultRenderer
|
|
157
|
+
};
|
|
158
|
+
function prefetchDefaultRenderer() {
|
|
159
|
+
DEFAULT_RENDER_LOADERS.default().catch(() => void 0);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/reader/navigation.ts
|
|
163
|
+
function locationAt(book, index, cfi) {
|
|
164
|
+
const chapter = Number.isInteger(index) ? book.chapters[index] : void 0;
|
|
165
|
+
if (chapter === void 0) {
|
|
166
|
+
throw new RangeError(
|
|
167
|
+
`\u7AE0\u7D22\u5F15\u8D8A\u754C\uFF1A${String(index)}\uFF08\u672C\u4E66\u5171 ${String(book.chapters.length)} \u7AE0\uFF09`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
return cfi === void 0 ? { href: chapter.href, index } : { href: chapter.href, index, cfi };
|
|
171
|
+
}
|
|
172
|
+
function locationForTocItem(book, item) {
|
|
173
|
+
const index = book.chapters.findIndex((chapter) => chapter.href === item.href);
|
|
174
|
+
if (index < 0) {
|
|
175
|
+
throw new chunkG7DLWGBW_cjs.FormatError(
|
|
176
|
+
`\u76EE\u5F55\u9879\u7684\u76EE\u6807\u4E0D\u5728\u9605\u8BFB\u987A\u5E8F\u91CC\uFF1A${item.href}\uFF08\u672C\u4E66\u5171 ${String(book.chapters.length)} \u7AE0\uFF09`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
const fragment = item.fragment?.trim() ?? "";
|
|
180
|
+
if (fragment === "") return { href: item.href, index };
|
|
181
|
+
if (fragment.startsWith("epubcfi(") || fragment.startsWith("/")) {
|
|
182
|
+
return { href: item.href, index, cfi: chunkGTGLDLJD_cjs.parseCfi(fragment) };
|
|
183
|
+
}
|
|
184
|
+
return { href: item.href, index, fragment };
|
|
185
|
+
}
|
|
186
|
+
function neighbourIndex(book, from, direction) {
|
|
187
|
+
let found;
|
|
188
|
+
for (let index = 0; index < book.chapters.length; index += 1) {
|
|
189
|
+
if (book.chapters[index]?.linear !== true) continue;
|
|
190
|
+
if (direction === 1) {
|
|
191
|
+
if (index > from) return index;
|
|
192
|
+
} else if (index < from) {
|
|
193
|
+
found = index;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return found;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/reader/open-options.ts
|
|
200
|
+
function buildFormatOpenOptions(input) {
|
|
201
|
+
const { signal, source, pdf } = input;
|
|
202
|
+
return {
|
|
203
|
+
signal,
|
|
204
|
+
...source === void 0 ? {} : { source },
|
|
205
|
+
...pdf === void 0 ? {} : { pdf }
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/reader/reader.ts
|
|
210
|
+
var DEFAULT_REGISTRY = createFormatRegistry(DEFAULT_FORMAT_LOADERS);
|
|
211
|
+
var Reader = class {
|
|
212
|
+
#options;
|
|
213
|
+
#machine = new ReaderStateMachine();
|
|
214
|
+
/**
|
|
215
|
+
* 当前会话 —— 渲染器 + 已解析的书 + 当前章索引。
|
|
216
|
+
*
|
|
217
|
+
* **合成一个对象而不是几个并列字段**(与 `render/default/index.ts` 的
|
|
218
|
+
* `ViewState` 同一理由):并列时「有渲染器但没有书」这类中间态在类型上合法,
|
|
219
|
+
* 于是每个用到它们的地方都要写一遍判空,而那些判空**永远不可达**。
|
|
220
|
+
*
|
|
221
|
+
* 它是**资源句柄**,不是状态标志位 —— 生命周期仍由状态机表达(H5)。
|
|
222
|
+
*/
|
|
223
|
+
#session;
|
|
224
|
+
/**
|
|
225
|
+
* 操作代次。`open()` 开始时自增,`close()` 也自增。
|
|
226
|
+
*
|
|
227
|
+
* 用途:让**在飞操作**知道自己是否已被 `close()` 接管。没有它,一次
|
|
228
|
+
* 「`close()` 之后 `open()` 才落地」会去执行 `closed → ready` —— 非法迁移,
|
|
229
|
+
* 于是把一次正常的取消变成契约外的 `StateError`。
|
|
230
|
+
*/
|
|
231
|
+
#generation = 0;
|
|
232
|
+
/** 当前在飞操作的取消控制器(`close()` 借它中止它们)。 */
|
|
233
|
+
#controller;
|
|
234
|
+
constructor(options = {}) {
|
|
235
|
+
this.#options = options;
|
|
236
|
+
}
|
|
237
|
+
/** 当前状态。只读 —— 只能由内部状态机迁移(H5)。 */
|
|
238
|
+
get state() {
|
|
239
|
+
return this.#machine.state;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* 当前书的目录树(按文档顺序)。无目录时为空数组。
|
|
243
|
+
*
|
|
244
|
+
* ## 为什么这个 getter 存在(T3.2b ③c 立项,ADR-0011 修订)
|
|
245
|
+
*
|
|
246
|
+
* 本节顶部的「刻意不提供的接口」原先写着**不暴露** `toc`。本批**改这条决定**,
|
|
247
|
+
* 理由是:**目录不可达就等于没有目录** —— `Reader` 的公开面原先**没有任何路径**
|
|
248
|
+
* 能让调用方拿到目录(`book` 也没暴露),于是「目录可跳转」这条需求在
|
|
249
|
+
* `Reader` 上**无法被调用方实现**(只能绕过门面直接用 `openBook` + 自备渲染器)。
|
|
250
|
+
* 现在有了 `toc` + {@link goTo} 的 `TocItem` 形态,这条链路才闭合。
|
|
251
|
+
*
|
|
252
|
+
* ⚠️ **只加这一个**:`book` / `currentLocation` 仍**不暴露** —— 它们各自都还没有
|
|
253
|
+
* 「调用方非拿到不可」的场景,而 `toc` 有。**未登记的能力不得擅自扩大公共面**(H13)。
|
|
254
|
+
*
|
|
255
|
+
* ## 前置条件:必须 `ready`
|
|
256
|
+
*
|
|
257
|
+
* 非 `ready` 时抛 `StateError`,**不返回空数组** —— 空数组与「这本书确实没有目录」
|
|
258
|
+
* 在类型上无法区分,返回它就是把**假信息**交给调用方(同 `locationAt` 的
|
|
259
|
+
* 「共 0 项」那条注释)。目录在 `open` 成功时一次性产出,`close` 后随会话一起释放。
|
|
260
|
+
*
|
|
261
|
+
* 返回的数组是**适配器产出的原对象**(不复制、不冻结):`TocItem` 是只读契约,
|
|
262
|
+
* 复制一份既无收益,又会让「同一个目录项对象」在两次读取间不是同一个引用。
|
|
263
|
+
*
|
|
264
|
+
* @throws {StateError} 当前状态不是 `ready`
|
|
265
|
+
*/
|
|
266
|
+
get toc() {
|
|
267
|
+
return this.#requireReadySession().opened.toc;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* 打开一本书。`idle → loading → ready`,失败则 `loading → error`。
|
|
271
|
+
*
|
|
272
|
+
* 输入归一化(`BookInput` 五种形态):`ArrayBuffer` / `Uint8Array` / `BookSource`
|
|
273
|
+
* 直接交给格式注册表(不复制);`Blob` 走 `arrayBuffer()`;`string` 按 **URL**
|
|
274
|
+
* 走 `fetch`。格式的判别(探测)与适配器的加载都在注册表里完成 —— 本方法
|
|
275
|
+
* 对「这是什么格式」一无所知(ADR-0013)。
|
|
276
|
+
*
|
|
277
|
+
* 打开成功后**立即渲染落点**:默认落到第 0 章(`Book.chapters[0]`),传了
|
|
278
|
+
* `ReaderOptions.initialLocation` 则落到该 CFI 所指的章与章内位置。
|
|
279
|
+
*
|
|
280
|
+
* @throws {StateError} 当前状态不是 `idle` / `error`,或 `ReaderOptions.host` 缺失
|
|
281
|
+
* @throws {ParseError} container / OPF / 目录文档 XML 结构损坏
|
|
282
|
+
* @throws {FormatError} 认不出格式、该格式的适配器尚未实现、归档结构非法、
|
|
283
|
+
* OPF 语义违规,或 `initialLocation` 无法解释为书内位置
|
|
284
|
+
* @throws {NetworkError} `string` 输入的 URL 取字节失败(非 2xx / 网络错误)
|
|
285
|
+
* @throws {AbortError} 操作被取消 —— **状态转 `closed`**,已分配资源全部释放
|
|
286
|
+
*/
|
|
287
|
+
async open(input, options = {}) {
|
|
288
|
+
if (!canTransition(this.#machine.state, "loading")) {
|
|
289
|
+
throw new chunkG7DLWGBW_cjs.StateError(
|
|
290
|
+
`Reader.open() \u53EA\u5141\u8BB8\u5728 idle / error \u72B6\u6001\u8C03\u7528\uFF0C\u5F53\u524D\u662F ${this.#machine.state}`
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
const host = this.#options.host;
|
|
294
|
+
if (host === void 0) {
|
|
295
|
+
throw new chunkG7DLWGBW_cjs.StateError("Reader.open() \u9700\u8981 ReaderOptions.host \u2014\u2014 \u6CA1\u6709\u5BBF\u4E3B\u5C31\u6CA1\u6709\u53EF\u6302\u8F7D\u7684\u89C6\u56FE");
|
|
296
|
+
}
|
|
297
|
+
const startCfi = resolveStartCfi(this.#options.initialLocation);
|
|
298
|
+
const generation = this.#generation += 1;
|
|
299
|
+
const controller = new AbortController();
|
|
300
|
+
this.#controller = controller;
|
|
301
|
+
const signal = linkOperationSignal(options.signal, controller);
|
|
302
|
+
this.#machine.transition("loading");
|
|
303
|
+
let mounted;
|
|
304
|
+
try {
|
|
305
|
+
chunkG7DLWGBW_cjs.throwIfAborted(signal);
|
|
306
|
+
if (this.#options.renderer === void 0) prefetchDefaultRenderer();
|
|
307
|
+
const opened = await this.#load(input, signal);
|
|
308
|
+
const renderer = this.#options.renderer ?? await createDefaultRendererFor(opened, this.#options.io);
|
|
309
|
+
mounted = renderer;
|
|
310
|
+
chunkG7DLWGBW_cjs.throwIfAborted(signal);
|
|
311
|
+
await renderer.mount(host, { signal });
|
|
312
|
+
chunkG7DLWGBW_cjs.throwIfAborted(signal);
|
|
313
|
+
const index = resolveStartIndex(opened.book, startCfi);
|
|
314
|
+
await renderer.render(locationAt(opened.book, index, startCfi), { signal });
|
|
315
|
+
chunkG7DLWGBW_cjs.throwIfAborted(signal);
|
|
316
|
+
this.#session = { renderer, opened, index };
|
|
317
|
+
this.#machine.transition("ready");
|
|
318
|
+
} catch (error) {
|
|
319
|
+
mounted?.unmount();
|
|
320
|
+
this.#settle(generation, chunkG7DLWGBW_cjs.isAbortError(error) ? "closed" : "error");
|
|
321
|
+
throw error;
|
|
322
|
+
} finally {
|
|
323
|
+
if (this.#controller === controller) this.#controller = void 0;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* 关闭并释放全部资源。任意非 `closed` 状态 → `closed`;`closed` 为终态,不可复用。
|
|
328
|
+
*
|
|
329
|
+
* **幂等且不抛**(`docs/API.md` §3.1):重复调用无副作用。
|
|
330
|
+
*
|
|
331
|
+
* 关闭会**中止在飞操作**(内部 `AbortController`)—— 被中止的 `open` 以其
|
|
332
|
+
* `AbortError` 拒绝,被中止的 `next` / `prev` / `goTo` 同样;两者的状态落点
|
|
333
|
+
* 都由迁移表决定(`loading → closed` / `paginating → closed`)。
|
|
334
|
+
*
|
|
335
|
+
* 只有**已挂载过**的渲染器会收到 `unmount()` —— 从未打开过时(`idle` 下
|
|
336
|
+
* 直接 `close()`)没有要释放的东西,不会去打扰调用方传入的自定义渲染器。
|
|
337
|
+
*/
|
|
338
|
+
close() {
|
|
339
|
+
if (this.#machine.state === "closed") return;
|
|
340
|
+
this.#generation += 1;
|
|
341
|
+
const controller = this.#controller;
|
|
342
|
+
this.#controller = void 0;
|
|
343
|
+
controller?.abort();
|
|
344
|
+
this.#machine.transition("closed");
|
|
345
|
+
const session = this.#session;
|
|
346
|
+
this.#session = void 0;
|
|
347
|
+
session?.renderer.unmount();
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* 下一章。仅 `ready` 状态可调用。
|
|
351
|
+
*
|
|
352
|
+
* **「下一章」= 从当前章索引向后第一个 `linear === true` 的章**
|
|
353
|
+
* (`linear="no"` 是封面 / 版权页这类辅助内容,不进阅读顺序)。
|
|
354
|
+
* 后面没有线性项时是**合法的 no-op**:不迁移、不抛、不再渲染 ——
|
|
355
|
+
* `docs/API.md` §3.1 没有给 `next` 登记 `RangeError`,且迁移表里没有
|
|
356
|
+
* `ready → ready` 这条边,两条合起来只允许「什么都不做」。
|
|
357
|
+
*
|
|
358
|
+
* @throws {StateError} 当前状态不是 `ready`
|
|
359
|
+
* @throws {AbortError} 操作被取消 —— **状态回 `ready`**,双缓冲不残留
|
|
360
|
+
*/
|
|
361
|
+
async next(options = {}) {
|
|
362
|
+
await this.#navigate((session) => {
|
|
363
|
+
const index = neighbourIndex(session.opened.book, session.index, 1);
|
|
364
|
+
return index === void 0 ? void 0 : locationAt(session.opened.book, index);
|
|
365
|
+
}, options);
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* 上一章。仅 `ready` 状态可调用。
|
|
369
|
+
*
|
|
370
|
+
* 与 {@link Reader.next} 对称:向前第一个 `linear === true` 的项;
|
|
371
|
+
* 前面没有线性项时是合法的 no-op。
|
|
372
|
+
*
|
|
373
|
+
* @throws {StateError} 当前状态不是 `ready`
|
|
374
|
+
* @throws {AbortError} 操作被取消 —— **状态回 `ready`**
|
|
375
|
+
*/
|
|
376
|
+
async prev(options = {}) {
|
|
377
|
+
await this.#navigate((session) => {
|
|
378
|
+
const index = neighbourIndex(session.opened.book, session.index, -1);
|
|
379
|
+
return index === void 0 ? void 0 : locationAt(session.opened.book, index);
|
|
380
|
+
}, options);
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* 跳转到指定位置。
|
|
384
|
+
*
|
|
385
|
+
* 四种目标形态:
|
|
386
|
+
*
|
|
387
|
+
* | 目标 | 语义 | 失败时 |
|
|
388
|
+
* | ---- | ---- | ------ |
|
|
389
|
+
* | `number` | **章索引**(`Book.chapters` 的下标,`0` 起) | 非整数 / 越界 → `RangeError` |
|
|
390
|
+
* | `Cfi` / CFI 字符串 | 书内 CFI,**按它的 spine 步推出章**,并作为章内位置 | 字符串不是 CFI,或 CFI 推不出章 → `FormatError`;推出的章号越界 → `RangeError` |
|
|
391
|
+
* | `TocItem` | **目录项**(T3.2b ③c):`href` → 章,`fragment` → 章内位置 | `href` 不在阅读顺序里 → `FormatError`(见 {@link locationForTocItem}) |
|
|
392
|
+
*
|
|
393
|
+
* **CFI → 章**的推导口径见 {@link spineIndexFromCfi};**目录项 → 章内位置**
|
|
394
|
+
* 的口径见 {@link locationForTocItem}(那里也登记了 MOBI 目录项当前
|
|
395
|
+
* 「落到章首」这个已知缺口)。
|
|
396
|
+
*
|
|
397
|
+
* `number` 形态**不检查** `linear` —— 索引直达是显式意图,与「翻页跳过辅助内容」分开。
|
|
398
|
+
*
|
|
399
|
+
* 跳到当前所在章**照常重新渲染**(不做特判):调用方拿到的是同一套
|
|
400
|
+
* 「渲染完成」保证,不需要自己判断「这算不算真的跳了」。
|
|
401
|
+
*
|
|
402
|
+
* @throws {StateError} 当前状态不是 `ready`
|
|
403
|
+
* @throws {RangeError} 索引越界(含 CFI 推出的章号越界)
|
|
404
|
+
* @throws {FormatError} 字符串目标不是合法 CFI / CFI 无法映射到书内的章 /
|
|
405
|
+
* 目录项的 `href` 不在阅读顺序里
|
|
406
|
+
* @throws {AbortError} 操作被取消 —— **状态回 `ready`**
|
|
407
|
+
*/
|
|
408
|
+
async goTo(target, options = {}) {
|
|
409
|
+
await this.#navigate((session) => resolveGoToTarget(session.opened.book, target), options);
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* 订阅状态变化,返回取消订阅函数。
|
|
413
|
+
*
|
|
414
|
+
* 取消函数**幂等**(重复调用无副作用)。监听器抛错**不**影响其他监听器
|
|
415
|
+
* —— 广播前会复制监听器集合(`state.ts` 已保证)。
|
|
416
|
+
*/
|
|
417
|
+
subscribe(listener) {
|
|
418
|
+
return this.#machine.subscribe(listener);
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* 取当前会话,同时完成 `next` / `prev` / `goTo` / `toc` 的前置检查。
|
|
422
|
+
*
|
|
423
|
+
* ⚠️ **两个条件写进同一个 `if`,是刻意的**:拆成两条时,第二条
|
|
424
|
+
* (「有会话」)在状态机正常工作时**永远走不到** —— `ready` / `paginating`
|
|
425
|
+
* 状态必然伴随会话。拆开就会留下一条不可达分支(`MEMORY.md` §7:
|
|
426
|
+
* 真不可达要**消掉它**,而不是标注忽略)。合并之后两个操作数**都有真实路径**:
|
|
427
|
+
* `idle` 下没有会话,`paginating` 下有会话但状态不对。
|
|
428
|
+
*/
|
|
429
|
+
#requireReadySession() {
|
|
430
|
+
const session = this.#session;
|
|
431
|
+
if (session === void 0 || this.#machine.state !== "ready") {
|
|
432
|
+
throw new chunkG7DLWGBW_cjs.StateError(
|
|
433
|
+
`next / prev / goTo / toc \u53EA\u5141\u8BB8\u5728 ready \u72B6\u6001\u8C03\u7528\uFF0C\u5F53\u524D\u662F ${this.#machine.state}`
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
return session;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* `next` / `prev` / `goTo` 的公共骨架。
|
|
440
|
+
*
|
|
441
|
+
* `resolve` 负责「目标从哪来」:`undefined` 表示**端点处没有可去的章**
|
|
442
|
+
* (合法的 no-op),此时不迁移、不渲染、不抛。
|
|
443
|
+
*
|
|
444
|
+
* `resolve` 抛出的 `RangeError` / `FormatError` 也**不会**触发迁移 ——
|
|
445
|
+
* 它跑在任何迁移之前,非法目标不该把状态推进到 `paginating`。
|
|
446
|
+
*/
|
|
447
|
+
async #navigate(resolve, options) {
|
|
448
|
+
const session = this.#requireReadySession();
|
|
449
|
+
chunkG7DLWGBW_cjs.throwIfAborted(options.signal);
|
|
450
|
+
const target = resolve(session);
|
|
451
|
+
if (target === void 0) return;
|
|
452
|
+
const generation = this.#generation;
|
|
453
|
+
const controller = new AbortController();
|
|
454
|
+
this.#controller = controller;
|
|
455
|
+
const signal = linkOperationSignal(options.signal, controller);
|
|
456
|
+
this.#machine.transition("paginating");
|
|
457
|
+
try {
|
|
458
|
+
await session.renderer.render(target, { signal });
|
|
459
|
+
chunkG7DLWGBW_cjs.throwIfAborted(signal);
|
|
460
|
+
session.index = target.index;
|
|
461
|
+
this.#settle(generation, "ready");
|
|
462
|
+
} catch (error) {
|
|
463
|
+
this.#settle(generation, chunkG7DLWGBW_cjs.isAbortError(error) ? "ready" : "error");
|
|
464
|
+
throw error;
|
|
465
|
+
} finally {
|
|
466
|
+
if (this.#controller === controller) this.#controller = void 0;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* 在飞操作结束时的状态落点。
|
|
471
|
+
*
|
|
472
|
+
* 代次已变 ⇒ `close()` 已经接管(它把状态带到了 `closed`)。此时**不再迁移**
|
|
473
|
+
* —— 再迁一次会抛 `StateError`(`closed` 无出边),把一次正常的取消
|
|
474
|
+
* 变成契约外的错误。
|
|
475
|
+
*/
|
|
476
|
+
#settle(generation, to) {
|
|
477
|
+
if (this.#generation === generation) {
|
|
478
|
+
this.#machine.transition(to);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* `BookInput` → 已解析的书。
|
|
483
|
+
*
|
|
484
|
+
* 前四种形态先归一到字节,第五种(`Uint8Array` / `BookSource`)直接交给注册表 ——
|
|
485
|
+
* 它用 `ArrayBuffer.isView`(跨 realm 安全)自行判别,不劳这里重复一遍。
|
|
486
|
+
* 格式的判别、适配器的加载与字节源的一次性读取都在注册表里完成。
|
|
487
|
+
*/
|
|
488
|
+
#load(input, signal) {
|
|
489
|
+
const openOptions = buildFormatOpenOptions({
|
|
490
|
+
signal,
|
|
491
|
+
...typeof input === "string" ? { source: input } : {},
|
|
492
|
+
pdf: this.#options.pdf
|
|
493
|
+
});
|
|
494
|
+
if (typeof input === "string") {
|
|
495
|
+
return fetchBookBytes(input, signal).then(
|
|
496
|
+
(bytes) => DEFAULT_REGISTRY.open(bytes, openOptions)
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
if (isBlob(input)) {
|
|
500
|
+
return input.arrayBuffer().then((buffer) => DEFAULT_REGISTRY.open(new Uint8Array(buffer), openOptions));
|
|
501
|
+
}
|
|
502
|
+
if (isArrayBuffer(input)) {
|
|
503
|
+
return DEFAULT_REGISTRY.open(new Uint8Array(input), openOptions);
|
|
504
|
+
}
|
|
505
|
+
return DEFAULT_REGISTRY.open(input, openOptions);
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
function linkOperationSignal(caller, controller) {
|
|
509
|
+
return caller === void 0 ? controller.signal : AbortSignal.any([controller.signal, caller]);
|
|
510
|
+
}
|
|
511
|
+
function isArrayBuffer(value) {
|
|
512
|
+
return Object.prototype.toString.call(value) === "[object ArrayBuffer]";
|
|
513
|
+
}
|
|
514
|
+
function isBlob(value) {
|
|
515
|
+
if (typeof value !== "object" || value === null) return false;
|
|
516
|
+
const candidate = value;
|
|
517
|
+
return typeof candidate.arrayBuffer === "function" && typeof candidate.size === "number";
|
|
518
|
+
}
|
|
519
|
+
async function fetchBookBytes(url, signal) {
|
|
520
|
+
let response;
|
|
521
|
+
try {
|
|
522
|
+
response = await fetch(url, { signal });
|
|
523
|
+
} catch (error) {
|
|
524
|
+
if (chunkG7DLWGBW_cjs.isAbortError(error)) throw chunkG7DLWGBW_cjs.createAbortError(error);
|
|
525
|
+
chunkG7DLWGBW_cjs.throwIfAborted(signal);
|
|
526
|
+
throw new chunkG7DLWGBW_cjs.NetworkError(`\u83B7\u53D6\u4E66\u6E90\u5931\u8D25\uFF1A${url}`, { cause: error });
|
|
527
|
+
}
|
|
528
|
+
chunkG7DLWGBW_cjs.throwIfAborted(signal);
|
|
529
|
+
if (!response.ok) {
|
|
530
|
+
throw new chunkG7DLWGBW_cjs.NetworkError(`\u83B7\u53D6\u4E66\u6E90\u5931\u8D25\uFF1A${url}\uFF08HTTP ${String(response.status)}\uFF09`);
|
|
531
|
+
}
|
|
532
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
533
|
+
}
|
|
534
|
+
async function createDefaultRendererFor(opened, io) {
|
|
535
|
+
const { book } = opened;
|
|
536
|
+
const createDefaultRenderer = await DEFAULT_RENDER_LOADERS.default();
|
|
537
|
+
return createDefaultRenderer({
|
|
538
|
+
loadChapter: (target) => opened.content.readText(target.href),
|
|
539
|
+
loadResource: createResourceLoader(opened, io),
|
|
540
|
+
language: book.metadata.language,
|
|
541
|
+
// exactOptionalPropertyTypes:可选字段不能显式传 `undefined` ⇒ 只在有值时加键
|
|
542
|
+
// (同 `render/default/index.ts` 的 `loadOptions`)。
|
|
543
|
+
...book.direction === void 0 ? {} : { direction: book.direction }
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
function createResourceLoader(opened, io) {
|
|
547
|
+
return async (archivePath, options) => {
|
|
548
|
+
try {
|
|
549
|
+
return io === void 0 ? await opened.content.read(archivePath, options) : await io.load(archivePath, options);
|
|
550
|
+
} catch (error) {
|
|
551
|
+
if (error instanceof chunkG7DLWGBW_cjs.FormatError) return void 0;
|
|
552
|
+
throw error;
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
function resolveStartCfi(location) {
|
|
557
|
+
if (location === void 0) return void 0;
|
|
558
|
+
return typeof location === "string" ? parseCfiTarget(location, "ReaderOptions.initialLocation") : location;
|
|
559
|
+
}
|
|
560
|
+
function resolveStartIndex(book, cfi) {
|
|
561
|
+
if (cfi === void 0) return 0;
|
|
562
|
+
const index = spineIndexFromCfi(cfi, book);
|
|
563
|
+
if (index === void 0) {
|
|
564
|
+
throw new chunkG7DLWGBW_cjs.FormatError(`initialLocation \u7684 CFI \u65E0\u6CD5\u6620\u5C04\u5230 spine \u4E2D\u7684\u7AE0\uFF1A${chunkGTGLDLJD_cjs.serializeCfi(cfi)}`);
|
|
565
|
+
}
|
|
566
|
+
return index;
|
|
567
|
+
}
|
|
568
|
+
function resolveGoToTarget(book, target) {
|
|
569
|
+
if (typeof target === "number") return locationAt(book, target);
|
|
570
|
+
if (typeof target === "object" && !chunkGTGLDLJD_cjs.isCfi(target)) return locationForTocItem(book, target);
|
|
571
|
+
const cfi = typeof target === "string" ? parseCfiTarget(target, "goTo") : target;
|
|
572
|
+
const index = spineIndexFromCfi(cfi, book);
|
|
573
|
+
if (index === void 0) {
|
|
574
|
+
throw new chunkG7DLWGBW_cjs.FormatError(`goTo \u7684 CFI \u65E0\u6CD5\u6620\u5C04\u5230 spine \u4E2D\u7684\u7AE0\uFF1A${chunkGTGLDLJD_cjs.serializeCfi(cfi)}`);
|
|
575
|
+
}
|
|
576
|
+
return locationAt(book, index, cfi);
|
|
577
|
+
}
|
|
578
|
+
function parseCfiTarget(value, label) {
|
|
579
|
+
const candidate = value;
|
|
580
|
+
if (!chunkGTGLDLJD_cjs.isCfiString(candidate)) {
|
|
581
|
+
throw new chunkG7DLWGBW_cjs.FormatError(`${label} \u7684\u5B57\u7B26\u4E32\u76EE\u6807\u4E0D\u662F\u5408\u6CD5\u7684 CFI\uFF1A${value}`);
|
|
582
|
+
}
|
|
583
|
+
return chunkGTGLDLJD_cjs.parseCfi(candidate);
|
|
584
|
+
}
|
|
585
|
+
function spineIndexFromCfi(cfi, book) {
|
|
586
|
+
const point = cfi.kind === "range" ? cfi.start : cfi;
|
|
587
|
+
const path = point.paths[0];
|
|
588
|
+
if (path === void 0) return void 0;
|
|
589
|
+
const itemref = path.steps[1];
|
|
590
|
+
if (itemref === void 0) return void 0;
|
|
591
|
+
if (itemref.id !== void 0) {
|
|
592
|
+
const byId = book.spine.findIndex((item) => item.idref === itemref.id);
|
|
593
|
+
if (byId !== -1) return byId;
|
|
594
|
+
}
|
|
595
|
+
if (path.steps[0]?.index !== 6) return void 0;
|
|
596
|
+
const step = itemref.index;
|
|
597
|
+
if (step < 2 || step % 2 !== 0) return void 0;
|
|
598
|
+
return step / 2 - 1;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
Object.defineProperty(exports, "AbortError", {
|
|
602
|
+
enumerable: true,
|
|
603
|
+
get: function () { return chunkG7DLWGBW_cjs.AbortError; }
|
|
604
|
+
});
|
|
605
|
+
Object.defineProperty(exports, "FormatError", {
|
|
606
|
+
enumerable: true,
|
|
607
|
+
get: function () { return chunkG7DLWGBW_cjs.FormatError; }
|
|
608
|
+
});
|
|
609
|
+
Object.defineProperty(exports, "FrondError", {
|
|
610
|
+
enumerable: true,
|
|
611
|
+
get: function () { return chunkG7DLWGBW_cjs.FrondError; }
|
|
612
|
+
});
|
|
613
|
+
Object.defineProperty(exports, "NetworkError", {
|
|
614
|
+
enumerable: true,
|
|
615
|
+
get: function () { return chunkG7DLWGBW_cjs.NetworkError; }
|
|
616
|
+
});
|
|
617
|
+
Object.defineProperty(exports, "ParseError", {
|
|
618
|
+
enumerable: true,
|
|
619
|
+
get: function () { return chunkG7DLWGBW_cjs.ParseError; }
|
|
620
|
+
});
|
|
621
|
+
Object.defineProperty(exports, "SecurityError", {
|
|
622
|
+
enumerable: true,
|
|
623
|
+
get: function () { return chunkG7DLWGBW_cjs.SecurityError; }
|
|
624
|
+
});
|
|
625
|
+
Object.defineProperty(exports, "StateError", {
|
|
626
|
+
enumerable: true,
|
|
627
|
+
get: function () { return chunkG7DLWGBW_cjs.StateError; }
|
|
628
|
+
});
|
|
629
|
+
Object.defineProperty(exports, "createAbortError", {
|
|
630
|
+
enumerable: true,
|
|
631
|
+
get: function () { return chunkG7DLWGBW_cjs.createAbortError; }
|
|
632
|
+
});
|
|
633
|
+
Object.defineProperty(exports, "isAbortError", {
|
|
634
|
+
enumerable: true,
|
|
635
|
+
get: function () { return chunkG7DLWGBW_cjs.isAbortError; }
|
|
636
|
+
});
|
|
637
|
+
Object.defineProperty(exports, "isFrondError", {
|
|
638
|
+
enumerable: true,
|
|
639
|
+
get: function () { return chunkG7DLWGBW_cjs.isFrondError; }
|
|
640
|
+
});
|
|
641
|
+
Object.defineProperty(exports, "linkSignals", {
|
|
642
|
+
enumerable: true,
|
|
643
|
+
get: function () { return chunkG7DLWGBW_cjs.linkSignals; }
|
|
644
|
+
});
|
|
645
|
+
Object.defineProperty(exports, "onAbort", {
|
|
646
|
+
enumerable: true,
|
|
647
|
+
get: function () { return chunkG7DLWGBW_cjs.onAbort; }
|
|
648
|
+
});
|
|
649
|
+
Object.defineProperty(exports, "throwIfAborted", {
|
|
650
|
+
enumerable: true,
|
|
651
|
+
get: function () { return chunkG7DLWGBW_cjs.throwIfAborted; }
|
|
652
|
+
});
|
|
653
|
+
exports.READER_STATES = READER_STATES;
|
|
654
|
+
exports.Reader = Reader;
|
|
655
|
+
exports.canTransition = canTransition;
|