@assetlib/sdk-expo 0.7.2-preview.1 → 0.7.3-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
@@ -1,8 +1,8 @@
1
1
  # @assetlib/sdk-expo
2
2
 
3
- Expo SDK 57 / React Native 0.86 image delivery adapter for `@assetlib/sdk-core`. One integration targets iOS, Android, and Expo web. This is a preview SDK, not separate Swift or Kotlin SDKs.
3
+ Expo SDK 54 to 57 (React Native 0.81 to 0.86) image delivery adapter for `@assetlib/sdk-core`. One integration targets iOS, Android, and Expo web. This is a preview SDK, not separate Swift or Kotlin SDKs.
4
4
 
5
- Install **`@assetlib/sdk-core` and `@assetlib/sdk-expo` at the same exact version** as direct dependencies, from npm (`preview` dist-tag) or as the tarballs attached to the same GitHub release. The Expo package depends on that exact core version. The host app needs Expo 57, React 19, React Native 0.86, and matching `expo-file-system` and `expo-image` versions:
5
+ Install **`@assetlib/sdk-core` and `@assetlib/sdk-expo` at the same exact version** as direct dependencies, from npm (`preview` dist-tag) or as the tarballs attached to the same GitHub release. The Expo package depends on that exact core version. The host app needs Expo SDK 54, 55, 56 or 57 with that SDK's React Native and React 19, and the `expo-file-system` and `expo-image` versions that SDK installs:
6
6
 
7
7
  ```sh
8
8
  npx expo install expo-file-system expo-image
@@ -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.7.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.7.3-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
 
@@ -251,4 +251,6 @@ Enabling telemetry changes the app's data collection behavior. Review Google Pla
251
251
 
252
252
  ## Validation
253
253
 
254
- `npm test` exercises component lifecycle behavior with a React test renderer and mocked platform boundaries, including state-family activation, pinned selection, cancellation, fallback, native no-file data URI generation, and `AssetlibVideo`'s reduced-motion, app-state, two-slot and failure handling against a simulated player. `npm run typecheck` checks against the current Expo 57 APIs. Core security and offline tests run in the sibling package. Actual iOS/Android runtime and web delivery checks must be reported separately; these tests and typechecking are not evidence of a native device run.
254
+ `npm test` exercises component lifecycle behavior with a React test renderer and mocked platform boundaries, including state-family activation, pinned selection, cancellation, fallback, native no-file data URI generation, and `AssetlibVideo`'s reduced-motion, app-state, two-slot and failure handling against a simulated player. `npm run typecheck` checks against Expo 57. Core security and offline tests run in the sibling package. Actual iOS/Android runtime and web delivery checks must be reported separately; these tests and typechecking are not evidence of a native device run.
255
+
256
+ Expo SDK 54 to 56, October 13, 2026: the source typechecked against the packages each SDK installs (Expo 54.0.0 and 54.0.37, 55.0.31, 56.0.23); only Expo 54 was also run. A new Expo SDK 54 app (React Native 0.81.5, expo-file-system 19.0.24, expo-image 3.0.11, expo-video 3.0.16) installed the packed packages without peer overrides and drew the public demo workspace's published images in a web export in Chrome 152, in a Release build on an iOS 27 Simulator and in a release build on an Android 16 emulator. Both native builds kept the verified cache after a restart, and on Android it restarted offline from that cache and drew the bundled images offline after its data was cleared. A new Expo SDK 57 app ran the same packed code in the same iOS Simulator and Android emulator release builds, including the replacement of an existing state file and cache entry and, on Android, an offline restart from the cache. Not checked: any runtime on Expo 55 or 56, offline runs on iOS, `AssetlibVideo` clip playback on Expo 54 to 56, and physical devices.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assetlib/sdk-expo",
3
- "version": "0.7.2-preview.1",
3
+ "version": "0.7.3-preview.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./src/index.tsx",
@@ -20,15 +20,15 @@
20
20
  "test": "node --test test/*.test.mjs"
21
21
  },
22
22
  "dependencies": {
23
- "@assetlib/sdk-core": "0.7.2-preview.1"
23
+ "@assetlib/sdk-core": "0.7.3-preview.1"
24
24
  },
25
25
  "peerDependencies": {
26
- "expo": ">=57 <58",
27
- "expo-file-system": ">=57 <58",
28
- "expo-image": ">=57 <58",
29
- "expo-video": ">=57 <58",
26
+ "expo": ">=54 <58",
27
+ "expo-file-system": "^19.0.11 || >=55 <58",
28
+ "expo-image": "^3.0.7 || >=55 <58",
29
+ "expo-video": "^3.0.11 || >=55 <58",
30
30
  "react": ">=19 <20",
31
- "react-native": ">=0.86 <0.87"
31
+ "react-native": ">=0.81 <0.87"
32
32
  },
33
33
  "peerDependenciesMeta": {
34
34
  "expo-video": {
@@ -18,6 +18,14 @@ function exclusive<T>(key: string, fn: () => Promise<T>): Promise<T> {
18
18
  locks.set(key, next.catch(() => {}));
19
19
  return next;
20
20
  }
21
+ // Expo 54 and 55 relocate synchronously, take no options and refuse an existing destination;
22
+ // Expo 56 and later return a promise. Deleting the destination first works on all of them (Expo
23
+ // 57's iOS `overwrite` does the same). State and install ID readers also accept the `.next`
24
+ // source, so a crash between the two steps loses neither.
25
+ async function replace(source: File, destination: File, mode: 'move' | 'copy' = 'move'): Promise<void> {
26
+ if (destination.exists) destination.delete();
27
+ await (mode === 'move' ? source.move(destination) : source.copy(destination));
28
+ }
21
29
  function directories(config: AssetlibConfig) {
22
30
  const root = new Directory(Paths.document, 'assetlib-v1', namespace(config));
23
31
  const images = new Directory(root, 'images');
@@ -47,7 +55,7 @@ export function createPlatformStorage(config: AssetlibConfig): AssetStorage {
47
55
  // written. A crash at either step cannot make this look like a fresh install.
48
56
  const marker = new File(root, 'migration.complete');
49
57
  marker.create({ overwrite: true }); marker.write('1');
50
- await next.move(new File(root, 'state.json'), { overwrite: true });
58
+ await replace(next, new File(root, 'state.json'));
51
59
  };
52
60
  const loadState = async () => {
53
61
  const { root, images } = directories(config);
@@ -75,7 +83,7 @@ export function createPlatformStorage(config: AssetlibConfig): AssetStorage {
75
83
  if (oldImages.exists) for (const entry of oldImages.list()) {
76
84
  if (!(entry instanceof File) || !/^[a-f0-9]{64}\.(webp|png)$/.test(entry.name) || entry.size > SDK_LIMITS.assetBytes || entry.size < 1) continue;
77
85
  if (count >= SDK_LIMITS.cacheEntries || bytes + entry.size > SDK_LIMITS.cacheBytes) continue;
78
- await entry.copy(new File(images, entry.name), { overwrite: true });
86
+ await replace(entry, new File(images, entry.name), 'copy');
79
87
  count++; bytes += entry.size;
80
88
  }
81
89
  await writeState(root, selected.raw);
@@ -112,7 +120,7 @@ export function createPlatformStorage(config: AssetlibConfig): AssetStorage {
112
120
  if (!/^[a-f0-9]{32}$/.test(value)) throw new Error('Invalid random install ID.');
113
121
  const next = new File(root, `${name}.next`);
114
122
  next.create({ overwrite: true }); next.write(value);
115
- await next.move(new File(root, `${name}.id`), { overwrite: true });
123
+ await replace(next, new File(root, `${name}.id`));
116
124
  return value;
117
125
  }),
118
126
  loadState: () => exclusive(scope, loadState),
@@ -152,7 +160,7 @@ export function createPlatformStorage(config: AssetlibConfig): AssetStorage {
152
160
  }
153
161
  const next = new File(images, `${hash}.next`);
154
162
  next.create({ overwrite: true }); next.write(bytes);
155
- await next.move(new File(images, `${hash}.${extension}`), { overwrite: true });
163
+ await replace(next, new File(images, `${hash}.${extension}`));
156
164
  }),
157
165
  // The player opens clips from files, so the core returns this URI instead of clip bytes.
158
166
  getAssetFileUri: async (hash, mime) => {
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.7.2-preview.1' },
51
+ sdk: telemetry.sdk ?? { name: 'sdk-expo', version: '0.7.3-preview.1' },
52
52
  build: telemetry.build ?? runtimeBuild() ?? { platform: 'expo', appVersion: 'unknown', buildNumber: 'unknown' },
53
53
  };
54
54
  }