@swiftbrowser/native 0.17.7 → 0.18.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/README.md +99 -11
- package/dist/blob.d.ts +60 -0
- package/dist/blob.d.ts.map +1 -0
- package/dist/blob.js +153 -0
- package/dist/blob.js.map +1 -0
- package/dist/bridge.d.ts +65 -1
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js.map +1 -1
- package/dist/fetch.d.ts +11 -3
- package/dist/fetch.d.ts.map +1 -1
- package/dist/fetch.js +26 -2
- package/dist/fetch.js.map +1 -1
- package/dist/hostRequests.d.ts +55 -0
- package/dist/hostRequests.d.ts.map +1 -0
- package/dist/hostRequests.js +196 -0
- package/dist/hostRequests.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/matchMedia.d.ts +43 -0
- package/dist/matchMedia.d.ts.map +1 -0
- package/dist/matchMedia.js +63 -0
- package/dist/matchMedia.js.map +1 -0
- package/dist/notifications.d.ts +108 -0
- package/dist/notifications.d.ts.map +1 -0
- package/dist/notifications.js +251 -0
- package/dist/notifications.js.map +1 -0
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +23 -2
- package/dist/run.js.map +1 -1
- package/dist/runtime.d.ts +2 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +69 -0
- package/dist/runtime.js.map +1 -1
- package/dist/secureStorage.d.ts +20 -0
- package/dist/secureStorage.d.ts.map +1 -0
- package/dist/secureStorage.js +28 -0
- package/dist/secureStorage.js.map +1 -0
- package/dist/site.d.ts +3 -3
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +1 -1
- package/dist/site.js.map +1 -1
- package/dist/subtle.d.ts +38 -0
- package/dist/subtle.d.ts.map +1 -0
- package/dist/subtle.js +72 -0
- package/dist/subtle.js.map +1 -0
- package/dist/undo.d.ts +23 -0
- package/dist/undo.d.ts.map +1 -0
- package/dist/undo.js +44 -0
- package/dist/undo.js.map +1 -0
- package/dist/webSocket.d.ts +73 -0
- package/dist/webSocket.d.ts.map +1 -0
- package/dist/webSocket.js +145 -0
- package/dist/webSocket.js.map +1 -0
- package/package.json +3 -3
- package/src/blob.ts +186 -0
- package/src/bridge.ts +74 -1
- package/src/fetch.ts +29 -3
- package/src/hostRequests.ts +216 -0
- package/src/index.ts +8 -0
- package/src/matchMedia.ts +79 -0
- package/src/notifications.ts +317 -0
- package/src/run.ts +21 -2
- package/src/runtime.ts +71 -0
- package/src/secureStorage.ts +39 -0
- package/src/site.ts +3 -3
- package/src/subtle.ts +87 -0
- package/src/undo.ts +47 -0
- package/src/webSocket.ts +175 -0
- package/swift/Sources/SwiftBrowserNative/Host/AudioRecorderBridge.swift +199 -0
- package/swift/Sources/SwiftBrowserNative/Host/FilesBridge.swift +154 -0
- package/swift/Sources/SwiftBrowserNative/Host/HostBridge.swift +211 -0
- package/swift/Sources/SwiftBrowserNative/Host/HostRequestBridge.swift +451 -0
- package/swift/Sources/SwiftBrowserNative/Host/NotificationBridge.swift +304 -0
- package/swift/Sources/SwiftBrowserNative/Host/SecureStorage.swift +60 -0
- package/swift/Sources/SwiftBrowserNative/Host/ShimAliases.swift +1 -0
- package/swift/Sources/SwiftBrowserNative/Host/TextMeasureBridge.swift +139 -0
- package/swift/Sources/SwiftBrowserNative/Host/UndoBridge.swift +170 -0
- package/swift/Sources/SwiftBrowserNative/Host/WebSocketBridge.swift +118 -0
- package/swift/Sources/SwiftBrowserNative/Runtime/NativeApp.swift +56 -0
- package/swift/Sources/SwiftBrowserNative/Runtime/NativeAppDelegate.swift +36 -0
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/Controls.swift +362 -16
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/DeleteBackward.swift +147 -0
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/KeyboardBar.swift +84 -0
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/More.swift +116 -11
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/Navigation.swift +12 -1
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/Shapes.swift +30 -9
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/Stacks.swift +163 -10
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/TextAndImages.swift +17 -4
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/Video.swift +8 -3
- package/swift/Sources/SwiftBrowserNative/Views/NativeRootView.swift +8 -0
- package/swift/Sources/SwiftBrowserNative/Views/StyleModifier.swift +41 -0
- package/swift/Sources/SwiftBrowserProtocol/Op.swift +27 -4
- package/swift/Sources/SwiftBrowserProtocol/Props.swift +110 -3
- package/swift/Sources/SwiftBrowserProtocol/RendererEvent.swift +47 -0
- package/swift/Sources/SwiftBrowserProtocol/SVGPath.swift +260 -0
- package/swift/Sources/SwiftBrowserProtocol/Style.swift +58 -0
- package/swift/Sources/SwiftBrowserTree/OpTree.swift +22 -2
- package/wasm/SwiftBrowserNative.wasm +2 -2
package/README.md
CHANGED
|
@@ -33,10 +33,11 @@ build against the exact version of the renderer this JavaScript was written
|
|
|
33
33
|
for. `swiftbrowser native init` lays a project out that way:
|
|
34
34
|
|
|
35
35
|
```sh
|
|
36
|
-
npx @swiftbrowser/cli native init MyApp # MyApp.xcodeproj,
|
|
37
|
-
cd MyApp
|
|
38
|
-
npm run build #
|
|
39
|
-
open
|
|
36
|
+
npx @swiftbrowser/cli native init MyApp # MyApp.xcodeproj, ios/, src/, web/
|
|
37
|
+
cd MyApp && npm install # React, @swiftbrowser/react, @swiftbrowser/web and this package
|
|
38
|
+
npm run build # ios/app.js, the bundle the app ships
|
|
39
|
+
open MyApp.xcodeproj # run it on a simulator
|
|
40
|
+
npm run dev:web # the same app as a web page
|
|
40
41
|
```
|
|
41
42
|
|
|
42
43
|
For a hand-made project:
|
|
@@ -71,8 +72,8 @@ run(<App />);
|
|
|
71
72
|
```
|
|
72
73
|
|
|
73
74
|
```sh
|
|
74
|
-
npx swiftbrowser-native build src/index.tsx --out ../
|
|
75
|
-
npx swiftbrowser-native dev src/index.tsx --out ../
|
|
75
|
+
npx swiftbrowser-native build src/index.tsx --out ../ios/app.js # the bundle Xcode ships
|
|
76
|
+
npx swiftbrowser-native dev src/index.tsx --out ../ios/app.js # http://localhost:5173/, Fast Refresh on save
|
|
76
77
|
```
|
|
77
78
|
|
|
78
79
|
On the Swift side, `NativeApp().load()` runs the bundled `app.js` and
|
|
@@ -93,13 +94,19 @@ headless path, which is right here; `@swiftbrowser/react`'s `Host` (React DOM
|
|
|
93
94
|
inside the tree) renders nothing, as `react-dom` is aliased to a stub.
|
|
94
95
|
|
|
95
96
|
`fetch` sends the request over the bridge and `URLSession` performs it;
|
|
96
|
-
bodies are whole (no streams,
|
|
97
|
-
|
|
97
|
+
bodies are whole (no streams), and may be a `Blob` or a `FormData` (sent as
|
|
98
|
+
multipart: a file upload works as written for the page). `URL` is a small
|
|
99
|
+
parser, enough for absolute and relative URLs and query strings, not the
|
|
100
|
+
whole WHATWG algorithm; `URL.createObjectURL` gives a `data:` URL. `WebSocket`
|
|
101
|
+
runs on `URLSessionWebSocketTask`, `crypto.subtle` digests (SHA-1 to
|
|
102
|
+
SHA-512) and signs HMACs on CryptoKit, `matchMedia` answers the
|
|
103
|
+
`prefers-color-scheme` queries from the host's environment, and
|
|
104
|
+
`navigator.vibrate` plays a haptic.
|
|
98
105
|
|
|
99
106
|
## Workers
|
|
100
107
|
|
|
101
108
|
```sh
|
|
102
|
-
npx swiftbrowser-native build src/index.tsx --out ../
|
|
109
|
+
npx swiftbrowser-native build src/index.tsx --out ../ios/app.js --worker render=src/render.worker.ts
|
|
103
110
|
```
|
|
104
111
|
|
|
105
112
|
```ts
|
|
@@ -174,6 +181,87 @@ lock screen needs; for the audio to go on with the screen locked, the app's
|
|
|
174
181
|
Xcode target also needs the `audio` background mode (Signing &
|
|
175
182
|
Capabilities → Background Modes → Audio, AirPlay, and Picture in Picture).
|
|
176
183
|
|
|
184
|
+
## Notifications
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
import { onNotification, registerForPush } from '@swiftbrowser/native';
|
|
188
|
+
|
|
189
|
+
if ((await Notification.requestPermission()) === 'granted') {
|
|
190
|
+
const token = await registerForPush();
|
|
191
|
+
await api.post('/devices', { token });
|
|
192
|
+
}
|
|
193
|
+
useEffect(() => onNotification((n) => { if (n.event === 'opened') openPost(n.data.postId); }), []);
|
|
194
|
+
new Notification('Saved', { body: 'The draft is on the server', tag: 'draft', data: { draftId } });
|
|
195
|
+
navigator.setAppBadge(unread);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The browser's `Notification` works in the native app over UserNotifications:
|
|
199
|
+
`Notification.permission` (read at launch and after each
|
|
200
|
+
`requestPermission`), `requestPermission()`, `new Notification(title, { body,
|
|
201
|
+
tag, data, silent, badge })` shown at once (`tag` is the notification's
|
|
202
|
+
identifier; a later one with the same tag replaces it), `close()`, and
|
|
203
|
+
`onclick` when the viewer opens it. `navigator.setAppBadge` and
|
|
204
|
+
`clearAppBadge` set the app badge.
|
|
205
|
+
|
|
206
|
+
`registerForPush()` registers with APNs and resolves with the device token as
|
|
207
|
+
hex, or rejects with the system's reason (the Xcode target needs the Push
|
|
208
|
+
Notifications capability, and the App file the delegate adaptor the project
|
|
209
|
+
`native init` writes has: `@UIApplicationDelegateAdaptor(NativeAppDelegate.self)`;
|
|
210
|
+
an app with a delegate of its own calls `NativeApp.didRegisterForRemoteNotifications(deviceToken:)`,
|
|
211
|
+
`didFailToRegisterForRemoteNotifications(error:)` and
|
|
212
|
+
`didReceiveRemoteNotification(userInfo:)` from it). `onNotification`
|
|
213
|
+
delivers what happens: `{ event: 'opened' | 'received', remote, id, title,
|
|
214
|
+
body, data, action? }`, with the push payload or the `Notification`'s `data`
|
|
215
|
+
as `data`. `opened` is the viewer tapping a notification (one that launched
|
|
216
|
+
the app waits for the first listener, like a URL); `received` is one that
|
|
217
|
+
arrived while the app is open (shown as it would be in the background) or a
|
|
218
|
+
silent push the app delegate was handed.
|
|
219
|
+
|
|
220
|
+
In the page (the round trip, or any browser) the same calls work with Web
|
|
221
|
+
Push: `Notification` is the browser's, `registerForPush({
|
|
222
|
+
applicationServerKey })` subscribes through the site's service worker with
|
|
223
|
+
the server's VAPID public key and resolves with the subscription's JSON as
|
|
224
|
+
the token, and the worker's pushes and clicks become `onNotification`
|
|
225
|
+
events. A server tells the two tokens apart by their shape and sends one
|
|
226
|
+
payload to both: APNs' (`aps.alert`, `aps.badge`), which the worker reads
|
|
227
|
+
too (docs/ops-protocol.md, "Push notifications").
|
|
228
|
+
|
|
229
|
+
## Sharing, picking, the clipboard and signing in
|
|
230
|
+
|
|
231
|
+
`ShareLink`, `fileImporter`, the clipboard and the color chooser from
|
|
232
|
+
`@swiftbrowser/react` ask the browser for a share sheet, a file chooser, the
|
|
233
|
+
pasteboard and a color picker; in the native app the host answers instead,
|
|
234
|
+
with `UIActivityViewController`, the photo picker (for pictures and videos)
|
|
235
|
+
or the document picker, `UIPasteboard` and `UIColorPickerViewController`.
|
|
236
|
+
Nothing to write: `run` fills the web renderer's environment with what the
|
|
237
|
+
page has nothing for, so a screen written for the page shares and picks as
|
|
238
|
+
it did. Picked files are `File`s with their bytes.
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
import { webAuthenticate, secureStorage } from '@swiftbrowser/native';
|
|
242
|
+
|
|
243
|
+
const callback = await webAuthenticate('https://x.example/oauth/authorize?…', { callbackScheme: 'myapp' });
|
|
244
|
+
secureStorage.setItem('session', new URL(callback).searchParams.get('token')!);
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
`webAuthenticate(url, { callbackScheme, ephemeral })` is the system's
|
|
248
|
+
sign-in sheet (`ASWebAuthenticationSession`): it resolves with the callback
|
|
249
|
+
URL the sign-in ends with, and rejects with an `AbortError` when the viewer
|
|
250
|
+
closes it. In the page it runs on the shim's session (the popup flow of
|
|
251
|
+
docs/ops-protocol.md, "Signing in"). `secureStorage` has `localStorage`'s
|
|
252
|
+
shape over the Keychain, for what must not sit in plain text; in the page
|
|
253
|
+
it is `localStorage` under a prefix.
|
|
254
|
+
|
|
255
|
+
## Undo
|
|
256
|
+
|
|
257
|
+
`useUndoManager()` from `@swiftbrowser/react` keeps its stack in
|
|
258
|
+
JavaScript; in the native app the window's `UndoManager` mirrors it (the
|
|
259
|
+
host's `undoState`, `Host/UndoBridge.swift`: one proxy action per side the
|
|
260
|
+
app can use, named as the app names it), so the shake, the three-finger
|
|
261
|
+
swipe, ⌘Z from a keyboard and the Edit menu undo and redo the app's
|
|
262
|
+
changes, as they do in a SwiftUI app. Nothing to write: `run` puts the
|
|
263
|
+
host in the web renderer's environment where the page's keyboard would be.
|
|
264
|
+
|
|
177
265
|
## The dev loop and Fast Refresh
|
|
178
266
|
|
|
179
267
|
`swiftbrowser-native dev` serves the bundle in two halves and a manifest:
|
|
@@ -218,8 +306,8 @@ DOM through `@swiftbrowser/react` directly, real SwiftUI in the iOS app, and
|
|
|
218
306
|
this: the iOS app's own views, in a page. `--base /path/` when the site is
|
|
219
307
|
served under a path of another site, `--module <file>` for a module built
|
|
220
308
|
from a checkout (`swift build --swift-sdk <wasm sdk> -c release --product
|
|
221
|
-
NativeRoundTrip`), `--name <App>` for the site's app name (the
|
|
222
|
-
|
|
309
|
+
NativeRoundTrip`), `--name <App>` for the site's app name (the project's
|
|
310
|
+
folder by default), `--catalog <dir.xcassets>` (repeatable) for the
|
|
223
311
|
app's named images and colors (the Xcode target's catalog, or one kept for
|
|
224
312
|
the site: a `.symbolset` holding any SVG draws as a symbol there), and
|
|
225
313
|
`--worker <name>=<entry>` (repeatable) for the app's workers, published
|
package/dist/blob.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Blob`, `File` and `FormData` for the native app: bytes with a type, a
|
|
3
|
+
* named one, and a form whose entries `fetch` sends as multipart. A body is
|
|
4
|
+
* still whole (no streams); `URL.createObjectURL` gives a `data:` URL, which
|
|
5
|
+
* the host's image loader and `URLSession` both read, and
|
|
6
|
+
* `revokeObjectURL` has nothing to free.
|
|
7
|
+
*/
|
|
8
|
+
export type BlobPartLike = string | ArrayBuffer | ArrayBufferView | SimpleBlob;
|
|
9
|
+
export interface BlobOptions {
|
|
10
|
+
type?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface FileOptions extends BlobOptions {
|
|
13
|
+
lastModified?: number;
|
|
14
|
+
}
|
|
15
|
+
/** The browser's `Blob`: bytes and a MIME type. */
|
|
16
|
+
export declare class SimpleBlob {
|
|
17
|
+
private readonly data;
|
|
18
|
+
readonly type: string;
|
|
19
|
+
constructor(parts?: Iterable<BlobPartLike>, options?: BlobOptions);
|
|
20
|
+
get size(): number;
|
|
21
|
+
/** The bytes (a view; callers must not write into it). */
|
|
22
|
+
bytes(): Uint8Array;
|
|
23
|
+
slice(start?: number, end?: number, contentType?: string): SimpleBlob;
|
|
24
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
25
|
+
text(): Promise<string>;
|
|
26
|
+
get [Symbol.toStringTag](): string;
|
|
27
|
+
}
|
|
28
|
+
/** The browser's `File`: a `Blob` with a name. */
|
|
29
|
+
export declare class SimpleFile extends SimpleBlob {
|
|
30
|
+
readonly name: string;
|
|
31
|
+
readonly lastModified: number;
|
|
32
|
+
constructor(parts: Iterable<BlobPartLike>, name: string, options?: FileOptions);
|
|
33
|
+
get [Symbol.toStringTag](): string;
|
|
34
|
+
}
|
|
35
|
+
export type FormDataEntryValueLike = string | SimpleFile;
|
|
36
|
+
/** The browser's `FormData`, as far as a request body needs it. */
|
|
37
|
+
export declare class SimpleFormData {
|
|
38
|
+
private entries_;
|
|
39
|
+
private static value;
|
|
40
|
+
append(name: string, value: unknown, filename?: string): void;
|
|
41
|
+
set(name: string, value: unknown, filename?: string): void;
|
|
42
|
+
get(name: string): FormDataEntryValueLike | null;
|
|
43
|
+
getAll(name: string): FormDataEntryValueLike[];
|
|
44
|
+
has(name: string): boolean;
|
|
45
|
+
delete(name: string): void;
|
|
46
|
+
entries(): IterableIterator<[string, FormDataEntryValueLike]>;
|
|
47
|
+
keys(): IterableIterator<string>;
|
|
48
|
+
values(): IterableIterator<FormDataEntryValueLike>;
|
|
49
|
+
forEach(callback: (value: FormDataEntryValueLike, name: string, form: SimpleFormData) => void): void;
|
|
50
|
+
[Symbol.iterator](): IterableIterator<[string, FormDataEntryValueLike]>;
|
|
51
|
+
get [Symbol.toStringTag](): string;
|
|
52
|
+
}
|
|
53
|
+
/** A multipart/form-data body for the form, with the boundary `random` makes unique. */
|
|
54
|
+
export declare function encodeMultipart(form: SimpleFormData, random: (count: number) => Uint8Array): {
|
|
55
|
+
bytes: Uint8Array;
|
|
56
|
+
contentType: string;
|
|
57
|
+
};
|
|
58
|
+
/** `URL.createObjectURL`: a `data:` URL holding the blob. */
|
|
59
|
+
export declare function objectURL(blob: SimpleBlob): string;
|
|
60
|
+
//# sourceMappingURL=blob.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blob.d.ts","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,WAAW,GAAG,eAAe,GAAG,UAAU,CAAC;AAE/E,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAqBD,mDAAmD;AACnD,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,KAAK,GAAE,QAAQ,CAAC,YAAY,CAAM,EAAE,OAAO,GAAE,WAAgB;IAOzE,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,0DAA0D;IAC1D,KAAK,IAAI,UAAU;IAInB,KAAK,CAAC,KAAK,SAAI,EAAE,GAAG,SAAY,EAAE,WAAW,SAAK,GAAG,UAAU;IAMzD,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;IAInC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAI7B,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,MAAM,CAEjC;CACF;AAED,kDAAkD;AAClD,qBAAa,UAAW,SAAQ,UAAU;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;gBAElB,KAAK,EAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB;IAMlF,IAAa,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,MAAM,CAE1C;CACF;AAED,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,UAAU,CAAC;AAEzD,mEAAmE;AACnE,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAA0C;IAE1D,OAAO,CAAC,MAAM,CAAC,KAAK;IAQpB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAI7D,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAY1D,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,sBAAsB,GAAG,IAAI;IAIhD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,sBAAsB,EAAE;IAI9C,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIzB,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IAI7D,IAAI,IAAI,gBAAgB,CAAC,MAAM,CAAC;IAIhC,MAAM,IAAI,gBAAgB,CAAC,sBAAsB,CAAC;IAInD,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,sBAAsB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,KAAK,IAAI,GAAG,IAAI;IAIpG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IAIvE,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,MAAM,CAEjC;CACF;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,UAAU,GAAG;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAavI;AAED,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAElD"}
|
package/dist/blob.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Blob`, `File` and `FormData` for the native app: bytes with a type, a
|
|
3
|
+
* named one, and a form whose entries `fetch` sends as multipart. A body is
|
|
4
|
+
* still whole (no streams); `URL.createObjectURL` gives a `data:` URL, which
|
|
5
|
+
* the host's image loader and `URLSession` both read, and
|
|
6
|
+
* `revokeObjectURL` has nothing to free.
|
|
7
|
+
*/
|
|
8
|
+
import { base64Encode, utf8Decode, utf8Encode } from './encoding';
|
|
9
|
+
function partBytes(part) {
|
|
10
|
+
if (typeof part === 'string')
|
|
11
|
+
return utf8Encode(part);
|
|
12
|
+
if (part instanceof SimpleBlob)
|
|
13
|
+
return part.bytes();
|
|
14
|
+
if (part instanceof ArrayBuffer)
|
|
15
|
+
return new Uint8Array(part.slice(0));
|
|
16
|
+
if (ArrayBuffer.isView(part))
|
|
17
|
+
return new Uint8Array(part.buffer.slice(part.byteOffset, part.byteOffset + part.byteLength));
|
|
18
|
+
return utf8Encode(String(part));
|
|
19
|
+
}
|
|
20
|
+
function concat(parts) {
|
|
21
|
+
const total = parts.reduce((sum, part) => sum + part.byteLength, 0);
|
|
22
|
+
const out = new Uint8Array(total);
|
|
23
|
+
let offset = 0;
|
|
24
|
+
for (const part of parts) {
|
|
25
|
+
out.set(part, offset);
|
|
26
|
+
offset += part.byteLength;
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
/** The browser's `Blob`: bytes and a MIME type. */
|
|
31
|
+
export class SimpleBlob {
|
|
32
|
+
data;
|
|
33
|
+
type;
|
|
34
|
+
constructor(parts = [], options = {}) {
|
|
35
|
+
this.data = concat([...parts].map(partBytes));
|
|
36
|
+
// As the browser does: lowercased, and empty when it is not printable ASCII.
|
|
37
|
+
const type = options.type ?? '';
|
|
38
|
+
this.type = /^[\x20-\x7e]*$/.test(type) ? type.toLowerCase() : '';
|
|
39
|
+
}
|
|
40
|
+
get size() {
|
|
41
|
+
return this.data.byteLength;
|
|
42
|
+
}
|
|
43
|
+
/** The bytes (a view; callers must not write into it). */
|
|
44
|
+
bytes() {
|
|
45
|
+
return this.data;
|
|
46
|
+
}
|
|
47
|
+
slice(start = 0, end = this.size, contentType = '') {
|
|
48
|
+
const from = start < 0 ? Math.max(this.size + start, 0) : Math.min(start, this.size);
|
|
49
|
+
const to = end < 0 ? Math.max(this.size + end, 0) : Math.min(end, this.size);
|
|
50
|
+
return new SimpleBlob([this.data.subarray(from, Math.max(from, to))], { type: contentType });
|
|
51
|
+
}
|
|
52
|
+
async arrayBuffer() {
|
|
53
|
+
return this.data.buffer.slice(this.data.byteOffset, this.data.byteOffset + this.data.byteLength);
|
|
54
|
+
}
|
|
55
|
+
async text() {
|
|
56
|
+
return utf8Decode(this.data);
|
|
57
|
+
}
|
|
58
|
+
get [Symbol.toStringTag]() {
|
|
59
|
+
return 'Blob';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** The browser's `File`: a `Blob` with a name. */
|
|
63
|
+
export class SimpleFile extends SimpleBlob {
|
|
64
|
+
name;
|
|
65
|
+
lastModified;
|
|
66
|
+
constructor(parts, name, options = {}) {
|
|
67
|
+
super(parts, options);
|
|
68
|
+
this.name = String(name);
|
|
69
|
+
this.lastModified = options.lastModified ?? Date.now();
|
|
70
|
+
}
|
|
71
|
+
get [Symbol.toStringTag]() {
|
|
72
|
+
return 'File';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** The browser's `FormData`, as far as a request body needs it. */
|
|
76
|
+
export class SimpleFormData {
|
|
77
|
+
entries_ = [];
|
|
78
|
+
static value(value, filename) {
|
|
79
|
+
if (value instanceof SimpleBlob) {
|
|
80
|
+
const name = filename ?? (value instanceof SimpleFile ? value.name : 'blob');
|
|
81
|
+
return value instanceof SimpleFile && filename === undefined ? value : new SimpleFile([value], name, { type: value.type });
|
|
82
|
+
}
|
|
83
|
+
return String(value);
|
|
84
|
+
}
|
|
85
|
+
append(name, value, filename) {
|
|
86
|
+
this.entries_.push([String(name), SimpleFormData.value(value, filename)]);
|
|
87
|
+
}
|
|
88
|
+
set(name, value, filename) {
|
|
89
|
+
const key = String(name);
|
|
90
|
+
const entry = [key, SimpleFormData.value(value, filename)];
|
|
91
|
+
const index = this.entries_.findIndex(([entryName]) => entryName === key);
|
|
92
|
+
if (index < 0) {
|
|
93
|
+
this.entries_.push(entry);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
this.entries_ = this.entries_.filter(([entryName], position) => position === index || entryName !== key);
|
|
97
|
+
this.entries_[index] = entry;
|
|
98
|
+
}
|
|
99
|
+
get(name) {
|
|
100
|
+
return this.entries_.find(([entryName]) => entryName === String(name))?.[1] ?? null;
|
|
101
|
+
}
|
|
102
|
+
getAll(name) {
|
|
103
|
+
return this.entries_.filter(([entryName]) => entryName === String(name)).map(([, value]) => value);
|
|
104
|
+
}
|
|
105
|
+
has(name) {
|
|
106
|
+
return this.entries_.some(([entryName]) => entryName === String(name));
|
|
107
|
+
}
|
|
108
|
+
delete(name) {
|
|
109
|
+
this.entries_ = this.entries_.filter(([entryName]) => entryName !== String(name));
|
|
110
|
+
}
|
|
111
|
+
*entries() {
|
|
112
|
+
yield* this.entries_;
|
|
113
|
+
}
|
|
114
|
+
*keys() {
|
|
115
|
+
for (const [name] of this.entries_)
|
|
116
|
+
yield name;
|
|
117
|
+
}
|
|
118
|
+
*values() {
|
|
119
|
+
for (const [, value] of this.entries_)
|
|
120
|
+
yield value;
|
|
121
|
+
}
|
|
122
|
+
forEach(callback) {
|
|
123
|
+
for (const [name, value] of this.entries_)
|
|
124
|
+
callback(value, name, this);
|
|
125
|
+
}
|
|
126
|
+
[Symbol.iterator]() {
|
|
127
|
+
return this.entries();
|
|
128
|
+
}
|
|
129
|
+
get [Symbol.toStringTag]() {
|
|
130
|
+
return 'FormData';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/** A multipart/form-data body for the form, with the boundary `random` makes unique. */
|
|
134
|
+
export function encodeMultipart(form, random) {
|
|
135
|
+
const boundary = `----SwiftBrowserFormBoundary${Array.from(random(16), (byte) => byte.toString(16).padStart(2, '0')).join('')}`;
|
|
136
|
+
const escape = (text) => text.replace(/\r/g, '%0D').replace(/\n/g, '%0A').replace(/"/g, '%22');
|
|
137
|
+
const parts = [];
|
|
138
|
+
for (const [name, value] of form.entries()) {
|
|
139
|
+
const head = [`--${boundary}`, `Content-Disposition: form-data; name="${escape(name)}"` + (typeof value === 'string' ? '' : `; filename="${escape(value.name)}"`)];
|
|
140
|
+
if (typeof value !== 'string')
|
|
141
|
+
head.push(`Content-Type: ${value.type || 'application/octet-stream'}`);
|
|
142
|
+
parts.push(utf8Encode(`${head.join('\r\n')}\r\n\r\n`));
|
|
143
|
+
parts.push(typeof value === 'string' ? utf8Encode(value) : value.bytes());
|
|
144
|
+
parts.push(utf8Encode('\r\n'));
|
|
145
|
+
}
|
|
146
|
+
parts.push(utf8Encode(`--${boundary}--\r\n`));
|
|
147
|
+
return { bytes: concat(parts), contentType: `multipart/form-data; boundary=${boundary}` };
|
|
148
|
+
}
|
|
149
|
+
/** `URL.createObjectURL`: a `data:` URL holding the blob. */
|
|
150
|
+
export function objectURL(blob) {
|
|
151
|
+
return `data:${blob.type || 'application/octet-stream'};base64,${base64Encode(blob.bytes())}`;
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=blob.js.map
|
package/dist/blob.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blob.js","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":"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"}
|
package/dist/bridge.d.ts
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* (Native/Sources/SwiftBrowserNative/Host/HostBridge.swift). Every call is
|
|
4
4
|
* synchronous; the ones that answer later do so by calling a global this
|
|
5
5
|
* package defines (`__sbTimerFired`, `__sbFetchResult`, `__sbImageProbed`,
|
|
6
|
-
* `__sbWorkerMessage`, `__sbOpenURL`, `__sbMediaSessionCommand
|
|
6
|
+
* `__sbWorkerMessage`, `__sbOpenURL`, `__sbMediaSessionCommand`,
|
|
7
|
+
* `__sbNotificationPermission`, `__sbPushRegistered`, `__sbNotification`,
|
|
8
|
+
* `__sbWebSocketEvent`, `__sbHostResponse`, `__sbUndoCommand`).
|
|
7
9
|
*/
|
|
8
10
|
export interface NativeBridge {
|
|
9
11
|
/** Applies a render pass's ops (a JSON array). Returns an error message, or nothing. */
|
|
@@ -43,6 +45,68 @@ export interface NativeBridge {
|
|
|
43
45
|
* `__sbMediaSessionCommand(json)`. Hosts before 0.19 lack it.
|
|
44
46
|
*/
|
|
45
47
|
mediaSession?(update: string): void;
|
|
48
|
+
/** The app listens: what came before it did (a tap that launched it) arrives at `__sbNotification(json)`. */
|
|
49
|
+
notificationsReady?(): void;
|
|
50
|
+
/** Answers `__sbNotificationPermission(id, 'default' | 'granted' | 'denied')`, after asking the viewer when `request` is set. */
|
|
51
|
+
notificationPermission?(id: number, request: boolean): void;
|
|
52
|
+
/** Shows `{ id, title, subtitle?, body?, data?, badge?, silent? }` now; returns what was wrong with it, or nothing. */
|
|
53
|
+
notificationShow?(json: string): string | null | undefined;
|
|
54
|
+
/** Closes the notifications with these identifiers (a JSON array). */
|
|
55
|
+
notificationClose?(json: string): void;
|
|
56
|
+
/** The app badge (0 clears it). */
|
|
57
|
+
setBadge?(count: number): void;
|
|
58
|
+
/**
|
|
59
|
+
* Registers for remote notifications; the device token (hex) or the
|
|
60
|
+
* failure answers `__sbPushRegistered(id, { token } | { error })`.
|
|
61
|
+
* `applicationServerKey` is for the page (the round trip's Web Push).
|
|
62
|
+
*/
|
|
63
|
+
pushRegister?(id: number, applicationServerKey?: string): void;
|
|
64
|
+
wsOpen?(id: number, url: string, protocols: string[]): void;
|
|
65
|
+
/** `{ text }` or `{ binary }` (base64), as JSON. */
|
|
66
|
+
wsSend?(id: number, json: string): void;
|
|
67
|
+
wsClose?(id: number, code: number, reason: string): void;
|
|
68
|
+
/** The base64 digest of base64 `data` under `algorithm` (`SHA-256`, …), or nothing when the host cannot. */
|
|
69
|
+
digest?(algorithm: string, data: string): string | null | undefined;
|
|
70
|
+
/** The base64 HMAC of base64 `data` with base64 `key` under the hash `algorithm`, or nothing. */
|
|
71
|
+
hmac?(algorithm: string, key: string, data: string): string | null | undefined;
|
|
72
|
+
/** `navigator.vibrate`: a haptic (`impact`, `selection`, `success`, `warning`, `error`). Hosts before 0.20 lack it. */
|
|
73
|
+
haptic?(kind: string): void;
|
|
74
|
+
/**
|
|
75
|
+
* A host request of the web renderer's kinds (hostRequests.ts): `share`,
|
|
76
|
+
* `pickFiles`, `clipboard`, `pickColor` and `webAuth`, with the request's
|
|
77
|
+
* params as JSON; the answer arrives at `__sbHostResponse(id, { value } | { error })`.
|
|
78
|
+
* Hosts before 0.20 lack it.
|
|
79
|
+
*/
|
|
80
|
+
hostRequest?(id: number, kind: string, json: string): void;
|
|
81
|
+
/**
|
|
82
|
+
* The app's undo manager's state (undo.ts): `{ canUndo, canRedo,
|
|
83
|
+
* undoActionName, redoActionName }` as JSON, which the host mirrors on the
|
|
84
|
+
* window's `UndoManager` so the system's undo reaches the app; the undo or
|
|
85
|
+
* redo the system asks for arrives at `__sbUndoCommand('undo' | 'redo')`.
|
|
86
|
+
* Hosts before 0.18 lack it.
|
|
87
|
+
*/
|
|
88
|
+
undoState?(json: string): void;
|
|
89
|
+
/**
|
|
90
|
+
* `measureText` (@swiftbrowser/react): the size `text` in `font` (a Font
|
|
91
|
+
* JSON object over the body font) takes wrapped to `width` points (one
|
|
92
|
+
* line when null), from `NSAttributedString`'s bounding rect, synchronously:
|
|
93
|
+
* `{ width, height, lines }`, or nothing when the host cannot measure.
|
|
94
|
+
* Hosts before 0.21 lack it.
|
|
95
|
+
*/
|
|
96
|
+
measureText?(text: string, font: string, width: number | null): {
|
|
97
|
+
width: number;
|
|
98
|
+
height: number;
|
|
99
|
+
lines: number;
|
|
100
|
+
} | null | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* The app's standard directories as `file:` URLs, a JSON object
|
|
103
|
+
* `{ documents, applicationSupport, caches, temporary }` (hostRequests.ts
|
|
104
|
+
* `files`, Host/FilesBridge.swift). Hosts before 0.22 lack it.
|
|
105
|
+
*/
|
|
106
|
+
appDirectories?(): string | null | undefined;
|
|
107
|
+
secureGet?(key: string): string | null;
|
|
108
|
+
secureSet?(key: string, value: string): void;
|
|
109
|
+
secureRemove?(key: string): void;
|
|
46
110
|
/**
|
|
47
111
|
* Starts worker `id` from `<name>.worker.js` in `engine` (`'context'`
|
|
48
112
|
* when absent; hosts before 0.16 take no engine); returns an error
|
package/dist/bridge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}
|
package/dist/bridge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAiJA,kEAAkE;AAClE,MAAM,UAAU,eAAe;IAC7B,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,OAAO,MAAM,EAAE,eAAe,EAAE,EAAE,IAAI,EAAE,CAAC;AAC3C,CAAC;AA0BD,yFAAyF;AACzF,MAAM,UAAU,YAAY,CAAC,IAAuB,UAAU;IAC5D,OAAQ,CAA8C,CAAC,UAAU,CAAC;AACpE,CAAC"}
|
package/dist/fetch.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `fetch`, `Headers`, `Request`, `Response` and `AbortController` over the
|
|
3
3
|
* bridge's `fetch` primitive (`URLSession` on the Swift side). Bodies cross
|
|
4
|
-
* as base64; a `Response` reads them back as text, JSON or
|
|
5
|
-
*
|
|
4
|
+
* as base64; a `Response` reads them back as text, JSON, bytes or a `Blob`.
|
|
5
|
+
* A `Blob` or `FormData` body (blob.ts) is sent whole, the form as
|
|
6
|
+
* multipart; there are no streams.
|
|
6
7
|
*/
|
|
7
8
|
import type { NativeBridge, NativeFetchResult } from './bridge';
|
|
9
|
+
import { SimpleBlob, SimpleFormData } from './blob';
|
|
8
10
|
type HeadersInitLike = SimpleHeaders | Record<string, string> | [string, string][];
|
|
9
11
|
export declare class SimpleHeaders {
|
|
10
12
|
private map;
|
|
@@ -45,11 +47,12 @@ export declare class SimpleAbortController {
|
|
|
45
47
|
readonly signal: SimpleAbortSignal;
|
|
46
48
|
abort(reason?: unknown): void;
|
|
47
49
|
}
|
|
48
|
-
type BodyInitLike = string | ArrayBuffer | ArrayBufferView | SimpleURLSearchParamsLike | null | undefined;
|
|
50
|
+
type BodyInitLike = string | ArrayBuffer | ArrayBufferView | SimpleURLSearchParamsLike | SimpleBlob | SimpleFormData | null | undefined;
|
|
49
51
|
interface SimpleURLSearchParamsLike {
|
|
50
52
|
toString(): string;
|
|
51
53
|
[Symbol.iterator](): IterableIterator<[string, string]>;
|
|
52
54
|
}
|
|
55
|
+
export declare function setBodyRandomness(random: (count: number) => Uint8Array): void;
|
|
53
56
|
declare class Body {
|
|
54
57
|
protected bytes: Uint8Array | null;
|
|
55
58
|
bodyUsed: boolean;
|
|
@@ -60,6 +63,9 @@ declare class Body {
|
|
|
60
63
|
bytesArray(): Promise<Uint8Array>;
|
|
61
64
|
text(): Promise<string>;
|
|
62
65
|
json(): Promise<unknown>;
|
|
66
|
+
blob(): Promise<SimpleBlob>;
|
|
67
|
+
/** The body's content type, for `blob()`; a Request or Response reads its headers. */
|
|
68
|
+
protected contentType(): string;
|
|
63
69
|
}
|
|
64
70
|
export interface RequestInitLike {
|
|
65
71
|
method?: string;
|
|
@@ -78,6 +84,7 @@ export declare class SimpleRequest extends Body {
|
|
|
78
84
|
}, init?: RequestInitLike);
|
|
79
85
|
/** @internal */
|
|
80
86
|
rawBytes(): Uint8Array | null;
|
|
87
|
+
protected contentType(): string;
|
|
81
88
|
clone(): SimpleRequest;
|
|
82
89
|
}
|
|
83
90
|
export interface ResponseInitLike {
|
|
@@ -95,6 +102,7 @@ export declare class SimpleResponse extends Body {
|
|
|
95
102
|
readonly redirected = false;
|
|
96
103
|
constructor(body?: BodyInitLike, init?: ResponseInitLike);
|
|
97
104
|
get ok(): boolean;
|
|
105
|
+
protected contentType(): string;
|
|
98
106
|
clone(): SimpleResponse;
|
|
99
107
|
static json(data: unknown, init?: ResponseInitLike): SimpleResponse;
|
|
100
108
|
static error(): SimpleResponse;
|
package/dist/fetch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"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"}
|
package/dist/fetch.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `fetch`, `Headers`, `Request`, `Response` and `AbortController` over the
|
|
3
3
|
* bridge's `fetch` primitive (`URLSession` on the Swift side). Bodies cross
|
|
4
|
-
* as base64; a `Response` reads them back as text, JSON or
|
|
5
|
-
*
|
|
4
|
+
* as base64; a `Response` reads them back as text, JSON, bytes or a `Blob`.
|
|
5
|
+
* A `Blob` or `FormData` body (blob.ts) is sent whole, the form as
|
|
6
|
+
* multipart; there are no streams.
|
|
6
7
|
*/
|
|
8
|
+
import { encodeMultipart, SimpleBlob, SimpleFormData } from './blob';
|
|
7
9
|
import { base64Decode, base64Encode, utf8Decode, utf8Encode } from './encoding';
|
|
8
10
|
export class SimpleHeaders {
|
|
9
11
|
map = new Map();
|
|
@@ -114,12 +116,21 @@ function timeoutError() {
|
|
|
114
116
|
error.name = 'TimeoutError';
|
|
115
117
|
return error;
|
|
116
118
|
}
|
|
119
|
+
/** The multipart boundary's randomness: the bridge's bytes once the runtime is installed, a counter before. */
|
|
120
|
+
let randomBytes = (count) => Uint8Array.from({ length: count }, (_, i) => (i * 37 + 11) & 255);
|
|
121
|
+
export function setBodyRandomness(random) {
|
|
122
|
+
randomBytes = random;
|
|
123
|
+
}
|
|
117
124
|
/** Bytes of a body, and the content type it implies when the caller set none. */
|
|
118
125
|
function encodeBody(body) {
|
|
119
126
|
if (body === null || body === undefined)
|
|
120
127
|
return { bytes: null, contentType: null };
|
|
121
128
|
if (typeof body === 'string')
|
|
122
129
|
return { bytes: utf8Encode(body), contentType: 'text/plain;charset=UTF-8' };
|
|
130
|
+
if (body instanceof SimpleBlob)
|
|
131
|
+
return { bytes: body.bytes(), contentType: body.type || null };
|
|
132
|
+
if (body instanceof SimpleFormData)
|
|
133
|
+
return encodeMultipart(body, randomBytes);
|
|
123
134
|
if (body instanceof ArrayBuffer)
|
|
124
135
|
return { bytes: new Uint8Array(body), contentType: null };
|
|
125
136
|
if (ArrayBuffer.isView(body))
|
|
@@ -158,6 +169,13 @@ class Body {
|
|
|
158
169
|
async json() {
|
|
159
170
|
return JSON.parse(utf8Decode(this.take()));
|
|
160
171
|
}
|
|
172
|
+
async blob() {
|
|
173
|
+
return new SimpleBlob([this.take()], { type: this.contentType() });
|
|
174
|
+
}
|
|
175
|
+
/** The body's content type, for `blob()`; a Request or Response reads its headers. */
|
|
176
|
+
contentType() {
|
|
177
|
+
return '';
|
|
178
|
+
}
|
|
161
179
|
}
|
|
162
180
|
export class SimpleRequest extends Body {
|
|
163
181
|
url;
|
|
@@ -179,6 +197,9 @@ export class SimpleRequest extends Body {
|
|
|
179
197
|
rawBytes() {
|
|
180
198
|
return this.bytes;
|
|
181
199
|
}
|
|
200
|
+
contentType() {
|
|
201
|
+
return this.headers.get('content-type') ?? '';
|
|
202
|
+
}
|
|
182
203
|
clone() {
|
|
183
204
|
return new SimpleRequest(this, { body: this.bytes });
|
|
184
205
|
}
|
|
@@ -203,6 +224,9 @@ export class SimpleResponse extends Body {
|
|
|
203
224
|
get ok() {
|
|
204
225
|
return this.status >= 200 && this.status < 300;
|
|
205
226
|
}
|
|
227
|
+
contentType() {
|
|
228
|
+
return this.headers.get('content-type') ?? '';
|
|
229
|
+
}
|
|
206
230
|
clone() {
|
|
207
231
|
return new SimpleResponse(this.bytes, { status: this.status, statusText: this.statusText, headers: this.headers, url: this.url });
|
|
208
232
|
}
|