libaribhtml5 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.
@@ -0,0 +1,57 @@
1
+ # 録画再生時の放送時計
2
+
3
+ データ放送アプリケーションは現在時刻を `new Date()` / `Date.now()` で取得します。
4
+ 録画再生でブラウザのシステム時計をそのまま返すと、録画当時ではなく再生日の日時が表示されます。
5
+
6
+ `AribReceiverHost.setBroadcastClock()` へ、放送時刻とメディア時刻の対応点を渡してください。
7
+ runtime は子 iframe に限って、引数なしの `new Date()`、`Date()`、`Date.now()` をこの時計へ
8
+ 置き換えます。`new Date(value)`、`Date.parse()`、`Date.UTC()` は通常の JavaScript と同じです。
9
+
10
+ ## tlvdemux WASM との接続
11
+
12
+ tlvdemux の `onBroadcastClock` は NTP epoch の絶対時刻と、それに対応するメディア時刻を返します。
13
+
14
+ ```js
15
+ const NTP_UNIX_EPOCH_SECONDS = 2208988800;
16
+
17
+ onBroadcastClock(clock) {
18
+ const epochMilliseconds =
19
+ Number(clock.broadcastTimeValue) * 1000 /
20
+ Number(clock.broadcastTimeTimescale) -
21
+ NTP_UNIX_EPOCH_SECONDS * 1000;
22
+
23
+ const mediaTimeSeconds =
24
+ Number(clock.mediaTimeValue) / Number(clock.mediaTimeTimescale);
25
+
26
+ host.setBroadcastClock({
27
+ epochMilliseconds,
28
+ mediaTimeSeconds,
29
+ currentMediaTimeSeconds: () => video.currentTime,
30
+ });
31
+ }
32
+ ```
33
+
34
+ runtime が返す時刻は次の式になります。
35
+
36
+ ```text
37
+ 録画内の現在時刻
38
+ = epochMilliseconds
39
+ + (video.currentTime - mediaTimeSeconds) * 1000
40
+ ```
41
+
42
+ このため、pause 中は時計も停止し、倍速再生では同じ倍率で進み、seek 後は移動先の放送時刻へ
43
+ 追従します。新しい入力またはチャンネルへ切り替える時は `host.clearBroadcastClock()` を呼びます。
44
+
45
+ NTP の値がまだ得られていない場合、`getBroadcastTime()` と子 iframe の Date はシステム時刻へ
46
+ フォールバックします。録画ファイルの更新日やファイル名から日時を推測しないでください。
47
+
48
+ ## 絶対時刻だけを持つ場合
49
+
50
+ メディア timeline がない入力では、受信した絶対時刻から通常速度で進む時計も設定できます。
51
+
52
+ ```ts
53
+ host.setBroadcastTime(new Date('2026-07-01T12:00:00+09:00'))
54
+ ```
55
+
56
+ この形式は pause、再生倍率、seek には追従しません。録画プレーヤーでは
57
+ `setBroadcastClock()` と `currentMediaTimeSeconds` を使用してください。
@@ -0,0 +1,143 @@
1
+ # Broadcast resources, Worker routing, and fonts
2
+
3
+ ## URL namespace
4
+
5
+ `AribReceiverHost` mounts receiver paths below `/data-broadcast/` by default:
6
+
7
+ ```ts
8
+ const host = new AribReceiverHost({
9
+ iframe,
10
+ viewport,
11
+ broadcastBaseUrl: '/data-broadcast/',
12
+ })
13
+
14
+ host.loadApplication('/sh4/40/001/startup/html/index.html')
15
+ // iframe: /data-broadcast/sh4/40/001/startup/html/index.html
16
+ ```
17
+
18
+ The base must be same-origin and end-user code may change it. The runtime
19
+ reports this stable scope as `receiverDevice.getSystemInformation().baseurl`.
20
+ Broadcaster startup libraries may append a selected mount such as `sh4`
21
+ themselves. Separately, HTML/CSS/runtime URL mapping derives the active mount
22
+ from the current document when resolving root-relative paths such as `/60/...`.
23
+
24
+ A production server or Service Worker should route only this prefix to its
25
+ carousel/VFS and remove the prefix before looking up the broadcast path:
26
+
27
+ ```text
28
+ /data-broadcast/sh4/60/001/image/logo.png
29
+ -> /sh4/60/001/image/logo.png in the carousel
30
+ ```
31
+
32
+ Keeping the prefix makes Worker scope and receiver-owned HTTP resources
33
+ explicit. `subt://` caption resources and `romsound://` receiver sounds are not
34
+ HTTP URLs and must not be rewritten into this namespace.
35
+
36
+ ## Optional browser VFS
37
+
38
+ The SDK build emits `arib-vfs-sw.js` and exports a matching client. The demuxer
39
+ or carousel owner feeds complete resources; application/context selection stays
40
+ outside the receiver runtime:
41
+
42
+ ```ts
43
+ const backend = new ServiceWorkerBroadcastVfs({
44
+ workerUrl: '/receiver/arib-vfs-sw.js',
45
+ baseUrl: '/data-broadcast/',
46
+ })
47
+ const vfs = new BroadcastVfsSession(backend)
48
+
49
+ await vfs.beginSession()
50
+ const revision = vfs.enqueue({ path, contentType, data })
51
+ await vfs.waitFor(revision)
52
+ await vfs.ensure(entry)
53
+ host.loadApplication(entry)
54
+ ```
55
+
56
+ `enqueue()` copies a callback-lifetime `Uint8Array` synchronously. Writes are
57
+ ordered in the background and its returned revision is an application launch
58
+ barrier. `ensure()` probes the Worker and, if its memory was reclaimed, begins
59
+ a fresh Worker session and replays the page-owned resource mirror. Demuxer
60
+ context selection and the `contextId -> revision` mapping stay in the player.
61
+
62
+ `ServiceWorkerBroadcastVfs.begin()`, `put()`, and `canRead()` remain available
63
+ as low-level backend operations for hosts which already own these guarantees.
64
+
65
+ The server which hosts the Worker script must permit the chosen scope. When the
66
+ script itself is outside `/data-broadcast/`, return this response header:
67
+
68
+ ```http
69
+ Service-Worker-Allowed: /data-broadcast/
70
+ ```
71
+
72
+ The main player page may intentionally live outside the Worker scope. VFS
73
+ mutations and probes therefore use direct messages to the scoped active Worker;
74
+ only the data-broadcast iframe's HTTP requests are intercepted. Cache Storage
75
+ preserves the active resource session if the browser reclaims or replaces the
76
+ Worker.
77
+
78
+ HTML and CSS preparation is shared by the development middleware and Worker.
79
+ `prepareBroadcastHtml()` handles the receiver object marker, ROM-sound deferral,
80
+ root-relative URL mapping, and runtime bootstrap. `prepareBroadcastStylesheet()`
81
+ maps root-relative CSS resources into the same namespace.
82
+
83
+ ## Stored-resource lifecycle
84
+
85
+ `navigator.receiverDevice.cacheEvent.storeDataResource()` now waits for the
86
+ complete response before dispatching `store_finished`. A failed response emits
87
+ `store_failed`; `releaseDataResource()` aborts an in-flight request. Page exit
88
+ releases every retained resource.
89
+
90
+ The default implementation fetches from the broadcast document, which lets a
91
+ Service Worker controlling `/data-broadcast/` handle and cache the request. A
92
+ native carousel or another VFS may instead provide `resourceStore`:
93
+
94
+ ```ts
95
+ const host = new AribReceiverHost({
96
+ iframe,
97
+ viewport,
98
+ resourceStore: {
99
+ async store(url, { signal }) {
100
+ await carousel.retain(url, signal)
101
+ },
102
+ release(url) {
103
+ return carousel.release(url)
104
+ },
105
+ },
106
+ })
107
+ ```
108
+
109
+ When a carousel module changes, notify the active application. An update is
110
+ downloaded again before its listener receives `updated`; deletion aborts and
111
+ releases it before the listener receives `deleted`.
112
+
113
+ ```ts
114
+ host.notifyDataResource('/sh4/60/001/font/service.woff', 'updated')
115
+ host.notifyDataResource('/sh4/60/001/font/service.woff', 'deleted')
116
+ ```
117
+
118
+ CSS fonts referenced by a d-data page are ordinary broadcast HTTP resources
119
+ and therefore follow the Worker route. A font explicitly retained through
120
+ `storeDataResource()` additionally follows the lifecycle above. TTML
121
+ `subt://n` fonts remain part of the caption data pipeline.
122
+
123
+ ## Receiver symbol font
124
+
125
+ `src/runtime/fonts/arib-symbols.svg` is the auditable SVG Font source for the
126
+ television-symbol block `U+1F19B..U+1F1AC`, including `U+1F19E SQUARED FOUR K`
127
+ (`🆞`). They are a minimal subset of the OFL-licensed Noto Sans Symbols font
128
+ used by the reference receiver. See `src/runtime/fonts/NOTICE.md` and
129
+ `src/runtime/fonts/OFL.txt`; no commercial font outline is included.
130
+
131
+ Modern browsers no longer reliably render SVG Fonts, so the same script emits
132
+ `arib-symbols.woff` for runtime use. The runtime registers the sparse font only
133
+ for its 18 code points under `ARIB Symbols` and the ARIB generic family names
134
+ `丸ゴシック`, `太丸ゴシック`, and `角ゴシック`. Ordinary Japanese glyphs continue
135
+ through the page's next fallback font. Dynamic DRCS and broadcaster-delivered
136
+ SVG/WOFF fonts stay separate from this static receiver font.
137
+
138
+ Regenerate both assets with:
139
+
140
+ ```sh
141
+ nix-shell -p python313Packages.fonttools --run \
142
+ 'python scripts/build-arib-symbol-font.py /path/to/NotoSansSymbols.ttf'
143
+ ```
@@ -0,0 +1,227 @@
1
+ # メディアスロットの使い方
2
+
3
+ ## まず結論
4
+
5
+ 通常は次のどれか一つを選びます。
6
+
7
+ | 用途 | 選ぶもの | できる重なり順 |
8
+ | --- | --- | --- |
9
+ | 通常ブラウザの demo / Web プレーヤー | `DomObjectMediaPlaneAdapter` | アプリ背景 `<` 映像 `<` アプリ UI |
10
+ | ネイティブ Surface / plugin / compositor | 独自 `AribMediaPlaneAdapter` | compositor の能力に依存 |
11
+ | 既存コードを当面維持 | `BehindIframeMediaPlaneAdapter` | 映像 `<` iframe 全体のみ |
12
+
13
+ caption ページを正しく表示するなら、1 番目または 2 番目が必要です。
14
+ `BehindIframeMediaPlaneAdapter` では実現できません。
15
+
16
+ ## ブラウザへ組み込む最小コード
17
+
18
+ 普通の動画表示領域とデータ放送 iframe を同じ viewport に置きます。
19
+
20
+ ```html
21
+ <div id="viewport">
22
+ <div id="normal-player">
23
+ <video id="video" autoplay muted playsinline></video>
24
+ </div>
25
+ <iframe id="broadcast" sandbox="allow-scripts allow-same-origin"></iframe>
26
+ </div>
27
+ ```
28
+
29
+ `DomObjectMediaPlaneAdapter` に既存の video と、データ放送を終了した時に video を戻す
30
+ 通常プレーヤー container を渡します。
31
+
32
+ ```ts
33
+ import {
34
+ AribReceiverHost,
35
+ DomObjectMediaPlaneAdapter,
36
+ type RuntimeWindow,
37
+ } from 'libaribhtml5'
38
+
39
+ const iframe = document.querySelector<HTMLIFrameElement>('#broadcast')!
40
+ const viewport = document.querySelector<HTMLElement>('#viewport')!
41
+ const normalPlayer = document.querySelector<HTMLElement>('#normal-player')!
42
+ const video = document.querySelector<HTMLVideoElement>('#video')!
43
+
44
+ const host = new AribReceiverHost({
45
+ iframe,
46
+ viewport,
47
+ mediaPlaneAdapter: new DomObjectMediaPlaneAdapter({
48
+ media: video,
49
+ normalPlayerContainer: normalPlayer,
50
+ }),
51
+ })
52
+
53
+ window.__ARIB_HTML5_INSTALL__ = (child: RuntimeWindow) => {
54
+ host.installRuntime(child)
55
+ }
56
+ ```
57
+
58
+ データ放送への出入りは次の二つだけです。
59
+
60
+ ```ts
61
+ // データ放送へ入る。video は放送 object の中へ移動する。
62
+ // The receiver path is mounted at /data-broadcast/sh4/... by default.
63
+ host.loadApplication('/sh4/40/001/startup/html/index.html')
64
+
65
+ // データ放送を終了する。iframe を閉じ、video を通常プレーヤーへ戻して表示する。
66
+ host.exitApplication()
67
+ ```
68
+
69
+ ページ内の「戻る」はアプリケーション内遷移です。`exitApplication()` ではありません。
70
+ テレビ UI の「データ放送終了」操作、チャンネル変更、または
71
+ `applicationManager.destroyApplication()` を受けた時だけ `exitApplication()` を呼びます。
72
+
73
+ ## 実行時に何が起きるか
74
+
75
+ ```text
76
+ 通常プレーヤー
77
+ host.loadApplication()
78
+ ↓
79
+ runtime が broadcast object を検出
80
+ mountMediaPlane(object, plane)
81
+ ↓
82
+ データ放送ページ内で object が移動・非表示
83
+ updateMediaPlane(object, plane)
84
+ ↓
85
+ 別のデータ放送 document へ遷移
86
+ unmountMediaPlane('document-unload') → 次ページで mount
87
+ ↓
88
+ host.exitApplication()
89
+ unmountMediaPlane('application-exit')
90
+ ↓
91
+ 通常プレーヤーへ video / Surface を戻す
92
+ ```
93
+
94
+ `DomObjectMediaPlaneAdapter` は `document-unload` では video を通常 container へ一時退避して
95
+ 非表示にします。`application-exit` の時だけ通常 container を全画面表示します。この区別に
96
+ より、データ放送のページ遷移中に普通のプレーヤー UI が点滅しません。
97
+
98
+ ## 独自 adapter を書く
99
+
100
+ ネイティブ Surface や compositor を使う場合の最小骨格です。
101
+
102
+ ```ts
103
+ import type {
104
+ AribMediaPlane,
105
+ AribMediaPlaneAdapter,
106
+ AribMediaPlaneUnmountReason,
107
+ } from 'libaribhtml5'
108
+
109
+ class NativeSurfaceAdapter implements AribMediaPlaneAdapter {
110
+ readonly renderMode = 'external' as const
111
+
112
+ mountMediaPlane(object: HTMLElement, plane: AribMediaPlane): void {
113
+ nativeSurface.bindBroadcastSource(plane.videoSource, plane.audioSource)
114
+ this.updateMediaPlane(object, plane)
115
+ }
116
+
117
+ updateMediaPlane(_object: HTMLElement, plane: AribMediaPlane): void {
118
+ nativeSurface.setVisible(plane.visible)
119
+ nativeSurface.setLogicalCanvas(plane.screenWidth, plane.screenHeight)
120
+ nativeSurface.setRect(plane.x, plane.y, plane.width, plane.height)
121
+ nativeSurface.setApplicationLayer(plane.layer)
122
+ }
123
+
124
+ unmountMediaPlane(reason: AribMediaPlaneUnmountReason): void {
125
+ if (reason === 'application-exit') {
126
+ nativeSurface.returnToNormalPlayer()
127
+ } else if (reason === 'host-destroy') {
128
+ nativeSurface.release()
129
+ } else {
130
+ nativeSurface.hide()
131
+ }
132
+ }
133
+ }
134
+ ```
135
+
136
+ `unmountMediaPlane` の reason は次の意味です。
137
+
138
+ | reason | adapter の動作 |
139
+ | --- | --- |
140
+ | `slot-removed` | decoder は維持し、Surface を隠す |
141
+ | `document-unload` | 次のデータ放送ページに備えて一時退避する |
142
+ | `application-exit` | 通常プレーヤーへ映像を戻して表示する |
143
+ | `host-destroy` | listener、Surface、decoder など所有資源を解放する |
144
+
145
+ 各メソッドは同じ状態で複数回呼ばれても壊れないように実装してください。
146
+
147
+ ## `AribMediaPlane` の読み方
148
+
149
+ ```ts
150
+ type AribMediaPlane = {
151
+ slotId: string
152
+ visible: boolean
153
+ x: number
154
+ y: number
155
+ width: number
156
+ height: number
157
+ screenWidth: number
158
+ screenHeight: number
159
+ videoSource?: string
160
+ audioSource?: string
161
+ layer: {
162
+ documentOrder: number
163
+ stackingPath: Array<{
164
+ tagName: string
165
+ id?: string
166
+ position: string
167
+ zIndex: string
168
+ display: string
169
+ visibility: string
170
+ opacity: number
171
+ transform: string
172
+ }>
173
+ }
174
+ }
175
+ ```
176
+
177
+ `x`、`y`、`width`、`height` は iframe の表示 pixel ではなく、アプリケーションの
178
+ 論理画布上の CSS pixel です。たとえば `screenWidth=3840`、`x=480` なら左端は 12.5% です。
179
+
180
+ `stackingPath` は html から object までの祖先を順番に格納します。ネイティブ compositor は
181
+ 単一の `zIndex` だけで判断せず、この path と `documentOrder` を使ってアプリケーションとの
182
+ 合成方針を決めます。ブラウザ用 `DomObjectMediaPlaneAdapter` では DOM 自身が合成するため、
183
+ この変換は不要です。
184
+
185
+ ## なぜ iframe 背面方式では不足するか
186
+
187
+ iframe と親 document の間で選べるのは次の順序だけです。
188
+
189
+ ```text
190
+ 映像 < iframe 全体
191
+ ```
192
+
193
+ 一方、caption ページの要求は次です。
194
+
195
+ ```text
196
+ iframe 内の背景 < 映像 < iframe 内の字幕説明ボタン
197
+ ```
198
+
199
+ 親側の z-index を何度変更しても、iframe の内部を二つに分割できません。
200
+ `DomObjectMediaPlaneAdapter` は video を object の fallback content にすることで解決します。
201
+ ネイティブ統合では compositor が同じ役割を担当します。
202
+
203
+ 旧 API の `videoSurface` だけを `AribReceiverHost` に渡すと、互換のため自動的に
204
+ `BehindIframeMediaPlaneAdapter` が選択されます。`attachVideo()` と `keepVideoVisible` も
205
+ 旧統合用です。新規コードでは使用しないでください。
206
+
207
+ ## 字幕
208
+
209
+ 通常の放送字幕は video / native Surface と同じプレーヤー側 renderer に置きます。
210
+ したがって media plane の移動、非表示、通常プレーヤーへの復帰へ一緒に追従します。
211
+
212
+ D Data の字幕コンテンツはアプリケーション DOM です。`setCaptionTracks()` と
213
+ `pushCaption()` で iframe へ渡し、通常の放送字幕として親側へ二重描画しないでください。
214
+
215
+ ---
216
+
217
+ ## English summary
218
+
219
+ Use `DomObjectMediaPlaneAdapter` for browser integration, implement
220
+ `AribMediaPlaneAdapter` for a native Surface/compositor, and use
221
+ `BehindIframeMediaPlaneAdapter` only as a limited compatibility fallback.
222
+
223
+ Call `host.loadApplication()` to enter data-broadcast mode. Call
224
+ `host.exitApplication()` to leave it; this produces
225
+ `unmountMediaPlane('application-exit')`, hides the iframe, and lets the adapter
226
+ return video to the ordinary player. A data-broadcast document navigation uses
227
+ `document-unload` instead and must not flash the ordinary player UI.
@@ -0,0 +1,92 @@
1
+ # Receiver host contracts
2
+
3
+ `libaribhtml5` owns the browser-facing receiver API. Stream and product data
4
+ remain owned by the integrating player and are supplied through
5
+ `AribReceiverHost`.
6
+
7
+ ## Program information
8
+
9
+ Pass decoded present/following event information with `host.setProgramInfo()`.
10
+ `start_time` and `f_start_time` must be JavaScript `Date` objects; `duration`
11
+ and `f_duration` are milliseconds.
12
+
13
+ ```ts
14
+ host.setProgramInfo({
15
+ original_network_id: event.originalNetworkId,
16
+ transport_stream_id: event.transportStreamId,
17
+ service_id: event.serviceId,
18
+ event_id: event.eventId,
19
+ event_name: event.title,
20
+ start_time: new Date(event.startTimeUnixMilliseconds),
21
+ duration: event.durationSeconds * 1000,
22
+ })
23
+ ```
24
+
25
+ Call `host.clearProgramInfo()` before changing input, service, or demux session.
26
+ Until the host supplies an event, `receiverDevice.getCurrentEventInformation()`
27
+ returns `null`. The SDK does not infer a service or video format from carousel
28
+ directory names such as `sh4` or `sh8`.
29
+
30
+ EIT parsing, present/following selection, AIT context selection, and synthetic
31
+ demo metadata belong to the integrating player rather than this SDK.
32
+
33
+ ## System information
34
+
35
+ Receiver identity and capability fields can be injected when the host is
36
+ created. Defaults come from the SDK package metadata.
37
+
38
+ ```ts
39
+ const host = new AribReceiverHost({
40
+ iframe,
41
+ viewport,
42
+ systemInformation: {
43
+ makerid: 'receiver-vendor',
44
+ modelname: 'Living Room Receiver',
45
+ decoder: 'native',
46
+ },
47
+ })
48
+ ```
49
+
50
+ `baseurl` is the stable receiver VFS scope, normally `/data-broadcast/`.
51
+ Broadcaster startup libraries may append their selected carousel mount (for
52
+ example `sh4`) themselves. The current document mount is used only when mapping
53
+ root-relative resources such as `/60/...`; it is not included in `baseurl`.
54
+ An injected `baseurl` cannot override the actual VFS scope.
55
+
56
+ ## Device identifiers
57
+
58
+ The default package combination remains `huggy`, `Huggy ARIB HTML5 Receiver`,
59
+ and the 48-bit identifier `4194c4ae4730`. A product host can resolve identifiers
60
+ by the requested receiver API kind without changing the runtime:
61
+
62
+ ```ts
63
+ const host = new AribReceiverHost({
64
+ iframe,
65
+ viewport,
66
+ getDeviceIdentifier: async kind => nativeReceiver.identifierFor(kind),
67
+ })
68
+ ```
69
+
70
+ Returning `null` means that identifier kind is unavailable. Provider failures
71
+ are isolated and reported to the application as an empty identifier.
72
+
73
+ ## Runtime lifecycle
74
+
75
+ `onStatus` is presentation text and may be localized. State machines must use
76
+ the typed `onLifecycle` callback instead:
77
+
78
+ ```ts
79
+ const host = new AribReceiverHost({
80
+ iframe,
81
+ viewport,
82
+ onStatus: text => statusLabel.textContent = text,
83
+ onLifecycle(event) {
84
+ if (event.type === 'installed') clearApplicationTimeout()
85
+ if (event.type === 'navigating') armApplicationTimeout()
86
+ if (event.type === 'error') reportRuntimeError(event.message)
87
+ },
88
+ })
89
+ ```
90
+
91
+ Lifecycle event types are `loading`, `installed`, `navigating`, `exited`,
92
+ `navigation-blocked`, `frame-blocked`, and `error`.
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "libaribhtml5",
3
+ "version": "0.1.0",
4
+ "description": "Browser runtime and receiver host for ARIB HTML5 data broadcasting",
5
+ "keywords": [
6
+ "arib",
7
+ "html5",
8
+ "data-broadcasting",
9
+ "bml",
10
+ "isdb",
11
+ "broadcast"
12
+ ],
13
+ "license": "MIT",
14
+ "author": {
15
+ "name": "huggy",
16
+ "url": "https://huggy.moe",
17
+ "email": "libaribhtml5@huggy.moe"
18
+ },
19
+ "aribReceiver": {
20
+ "modelName": "Huggy ARIB HTML5 Receiver"
21
+ },
22
+ "type": "module",
23
+ "main": "./dist/sdk/libaribhtml5.mjs",
24
+ "module": "./dist/sdk/libaribhtml5.mjs",
25
+ "types": "./dist/types/index.d.ts",
26
+ "unpkg": "./dist/sdk/libaribhtml5.js",
27
+ "jsdelivr": "./dist/sdk/libaribhtml5.js",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/types/index.d.ts",
31
+ "import": "./dist/sdk/libaribhtml5.mjs",
32
+ "default": "./dist/sdk/libaribhtml5.mjs"
33
+ },
34
+ "./iife": "./dist/sdk/libaribhtml5.js",
35
+ "./arib-vfs-sw.js": "./dist/sdk/arib-vfs-sw.js",
36
+ "./package.json": "./package.json"
37
+ },
38
+ "files": [
39
+ "dist/sdk",
40
+ "dist/types",
41
+ "docs",
42
+ "LICENSE",
43
+ "README.md",
44
+ "THIRD_PARTY_NOTICES.md"
45
+ ],
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "git+https://github.com/makeding/libaribhtml5.git"
49
+ },
50
+ "bugs": {
51
+ "url": "https://github.com/makeding/libaribhtml5/issues"
52
+ },
53
+ "homepage": "https://github.com/makeding/libaribhtml5#readme",
54
+ "publishConfig": {
55
+ "access": "public"
56
+ },
57
+ "devDependencies": {
58
+ "typescript": "^5.8.0",
59
+ "vite": "^7.0.0"
60
+ },
61
+ "scripts": {
62
+ "dev": "vite --host 127.0.0.1",
63
+ "test": "node --experimental-strip-types scripts/test-runtime-contracts.mjs && node --experimental-strip-types scripts/test-vfs-worker.mjs",
64
+ "build": "tsc --noEmit && vite build && vite build --config vite.sdk.config.mjs && vite build --config vite.worker.config.mjs",
65
+ "build:sdk": "tsc --noEmit && vite build --config vite.sdk.config.mjs && vite build --config vite.worker.config.mjs && npm run build:types",
66
+ "build:types": "tsc -p tsconfig.build.json && node scripts/rewrite-declaration-imports.mjs",
67
+ "preview": "vite preview --host 127.0.0.1"
68
+ }
69
+ }