@assetlib/sdk-expo 0.6.2-preview.1 → 0.6.4-preview.1

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 CHANGED
@@ -192,7 +192,7 @@ There is no automatic app-data reset API: disconnect the client to show bundled
192
192
 
193
193
  ## Short looping video
194
194
 
195
- `AssetlibVideo` shows a placement's poster and plays its signed short looping MP4 clip over it. It ships from a separate entry point so `expo-video` stays an optional peer dependency; importing `@assetlib/sdk-expo` never loads it. **This has not been verified on a device, simulator, emulator or browser**; it is covered by Node unit tests with a simulated player only.
195
+ `AssetlibVideo` shows a placement's poster and plays its signed short looping MP4 clip over it. It ships from a separate entry point so `expo-video` stays an optional peer dependency; importing `@assetlib/sdk-expo` never loads it. On October 11, 2026 it was checked with expo-video 57.0.5 on an iOS 27 Simulator and an Android 16 emulator (0.6.0-preview.1), and in headless Chrome 154 (0.6.2-preview.1). The checks covered playback from the SDK's cache, looping, offline replay and reduced motion. The same day, the 0.6.4-preview.1 change was checked in Safari: headless Playwright WebKit 26.6, desktop Safari 27.0.1 through `safaridriver`, and Mobile Safari 27.0 on an iOS 27 Simulator. In all three the poster hides once playback starts, and in WebKit a rejected `play()` keeps the poster. Offline replay in desktop Safari was last checked on 0.6.2-preview.1, and reduced motion only in WebKit and Mobile Safari. It has not been tested on a physical device.
196
196
 
197
197
  ```sh
198
198
  npx expo install expo-video
@@ -211,13 +211,13 @@ import { AssetlibVideo } from '@assetlib/sdk-expo/video';
211
211
  />
212
212
  ```
213
213
 
214
- - **Poster first.** It takes `AssetlibImage`'s props and renders that image as the poster, bundled fallback first. `style` sizes the container; the poster and clip fill it. The poster stays visible until the clip's first frame renders and returns on any failure.
214
+ - **Poster first.** It takes `AssetlibImage`'s props and renders that image as the poster, bundled fallback first. `style` sizes the container; the poster and clip fill it. The poster stays visible until the clip's first frame renders (on web, also once the `<video>` element's `play()` resolves, because Safari never sends expo-video's first-frame event) and returns on any failure.
215
215
  - **Playback.** `expo-video`'s `useVideoPlayer` with `loop` and `muted` (screen not kept awake), in a `VideoView` with `nativeControls={false}` and the poster's `contentFit` (`none` and `scale-down` become `contain`).
216
216
  - **When it plays.** Only while `AccessibilityInfo.isReduceMotionEnabled()` reports `false` (it listens to `reduceMotionChanged`; an unknown or failing query counts as reduced), the app is active (`AppState`; `background` and `inactive` stop it), `paused` is not set, and the view holds one of two playback slots shared by every `AssetlibVideo` in the app. Other views show their poster and take a slot when one frees. A view resolves its clip only once it holds a slot. Off-screen detection is up to the app: pass `paused` for views that are not visible.
217
217
  - **Failures.** A missing clip, a failed check, a player error, or a rejected web `play()` (autoplay blocked, for example in iOS Low Power Mode) shows the poster until the request changes (client, asset, `revision` or `cachePolicy`).
218
218
  - **iOS and Android.** The native adapter caches verified clips as `<sha256>.mp4` in the namespaced cache directory (at most 4 MiB each, inside the 50 MiB / 100 entry budget) and the core returns the file's `file://` URI. Under the `memory` and `none` policies `resolveVideo` returns the poster without downloading, because the player needs a file.
219
219
  - **Web.** Verified bytes from IndexedDB, memory or the network become a `video/mp4` Blob URL, revoked when the clip changes or the component unmounts.
220
- - **Open risk.** `expo-video` documents `require()` assets and media-library URIs as local sources, not plain `file://` URIs. Its 57.0.5 source passes a string URI through unchanged: iOS builds an `AVURLAsset` from it (only `ph://` is special-cased) and Android hands it to Media3's `DefaultDataSource`, which reads `file://` URIs. Whether a cached file plays on devices is unverified.
220
+ - **Cached `file://` clips.** `expo-video` documents `require()` assets and media-library URIs as local sources, not plain `file://` URIs. Its 57.0.5 source passes a string URI through unchanged. On October 11, 2026 it played the SDK's cached `file://` clip on an iOS 27 Simulator and an Android 16 emulator, both right after the download and offline after a relaunch. Physical devices and other expo-video versions are unverified.
221
221
  - Limits and the signed descriptor are in the core README's [Short looping video](../sdk-core/README.md#short-looping-video) section. `createExpoAssetClient(config, { videoTimeoutMs })` sets the clip deadline.
222
222
 
223
223
 
@@ -237,7 +237,7 @@ await client.flush();
237
237
  client.dispose(); // Remove the AppState listener and stop observation timers.
238
238
  ```
239
239
 
240
- The adapter identifies itself as `sdk-expo` version `0.6.2-preview.1`. An explicit `telemetry.sdk` or `telemetry.build` takes precedence. When the runtime can optionally require `expo-application` or `expo-constants`, the adapter reads native app version and build number (falling back to Expo config version/build). Missing packages, unavailable runtime loaders, and throwing native bridges are ignored; no new dependency is required. Supply `build` explicitly for reliable production attribution, especially with bundlers that cannot resolve optional runtime requires or in Expo Go. Without complete runtime metadata or an explicit build, batches use platform `expo` and version/build `unknown`.
240
+ The adapter identifies itself as `sdk-expo` version `0.6.4-preview.1`. An explicit `telemetry.sdk` or `telemetry.build` takes precedence. When the runtime can optionally require `expo-application` or `expo-constants`, the adapter reads native app version and build number (falling back to Expo config version/build). Missing packages, unavailable runtime loaders, and throwing native bridges are ignored; no new dependency is required. Supply `build` explicitly for reliable production attribution, especially with bundlers that cannot resolve optional runtime requires or in Expo Go. Without complete runtime metadata or an explicit build, batches use platform `expo` and version/build `unknown`.
241
241
 
242
242
  Only the observation contract fields are sent: environment, SDK name/version, build platform/version/number, random install ID, send time, and coalesced events with placement key, kind/count, source, asset ID/release sequence when known, effective arm/appearance when selected, and fallback reason. Bundle outcomes send only `fallback` events without asset ID or sequence. Dynamic catalog references do not emit observations because they have no placement key. Image bytes, descriptions, URLs, device identifiers, account identifiers, and status/error messages are not included.
243
243
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assetlib/sdk-expo",
3
- "version": "0.6.2-preview.1",
3
+ "version": "0.6.4-preview.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./src/index.tsx",
@@ -20,7 +20,7 @@
20
20
  "test": "node --test test/*.test.mjs"
21
21
  },
22
22
  "dependencies": {
23
- "@assetlib/sdk-core": "0.6.2-preview.1"
23
+ "@assetlib/sdk-core": "0.6.4-preview.1"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "expo": ">=57 <58",
package/src/telemetry.ts CHANGED
@@ -48,7 +48,7 @@ export function expoTelemetry(telemetry: ClientOptions['telemetry']): ClientOpti
48
48
  if (!telemetry?.enabled) return telemetry;
49
49
  return {
50
50
  ...telemetry,
51
- sdk: telemetry.sdk ?? { name: 'sdk-expo', version: '0.6.2-preview.1' },
51
+ sdk: telemetry.sdk ?? { name: 'sdk-expo', version: '0.6.4-preview.1' },
52
52
  build: telemetry.build ?? runtimeBuild() ?? { platform: 'expo', appVersion: 'unknown', buildNumber: 'unknown' },
53
53
  };
54
54
  }
package/src/video.tsx CHANGED
@@ -66,8 +66,10 @@ function LoopingClip({ uri, contentFit, onFirstFrame, onFailure }: { uri: string
66
66
  try {
67
67
  if (player.status === 'error') fail();
68
68
  // On web, VideoView exposes its <video> element, whose play() promise rejects when autoplay is blocked.
69
+ // It resolves once playback starts, which also counts as the first frame: Safari fires loadstart before
70
+ // expo-video's web VideoView listens for it, so onFirstFrameRender never arrives there.
69
71
  const element = (view.current as unknown as { nativeRef?: { current?: PlayableElement | null } } | null)?.nativeRef?.current;
70
- if (typeof element?.play === 'function') Promise.resolve(element.play()).catch(fail);
72
+ if (typeof element?.play === 'function') Promise.resolve(element.play()).then(() => { if (mounted) callbacks.current.onFirstFrame(); }, fail);
71
73
  else player.play();
72
74
  } catch { fail(); }
73
75
  return () => { mounted = false; subscription.remove(); };