@assetlib/sdk-core 0.6.4-preview.1 → 0.7.0-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 +5 -5
- package/dist/index.js +6 -0
- package/dist/telemetry.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -100,7 +100,7 @@ A catalog placement can declare `"rendering": "template"`. Assetlib then deliver
|
|
|
100
100
|
{ "key": "tab.trips", "symbol": ["Tabs", "trips"], "width": 24, "height": 24, "rendering": "template" }
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
Signed image descriptors (placements, state members, variant cells and catalog page images) may carry `rendering`. A present value that is not a string matching `^[a-z][a-z0-9-]{0,31}$` rejects the release; absent means `original`. Resolution compares the reference's rendering with the descriptor actually selected after arm, appearance and state selection. If they differ, or the descriptor names a value this client does not know, that release's descriptor is skipped as for an incompatible size: its cache entry is not read and nothing is downloaded.
|
|
103
|
+
Signed image descriptors (placements, state members, variant cells and catalog page images) may carry `rendering`. A present value that is not a string matching `^[a-z][a-z0-9-]{0,31}$` rejects the release; absent means `original`. Resolution compares the reference's rendering with the descriptor actually selected after arm, appearance and state selection. If they differ, or the descriptor names a value this client does not know, that release's descriptor is skipped as for an incompatible size: its cache entry is not read and nothing is downloaded. When the current release publishes the key, resolution continues with the usual order, a compatible verified cached image from an older retained release and then the bundled fallback (`fallbackReason: 'missing'`). Other placements are unaffected. Catalog page images resolve as `original`. `ResolvedAsset.rendering` reports the delivered descriptor's rendering, `'original'` or `'template'`.
|
|
104
104
|
|
|
105
105
|
The bundled fallback must also be a single-color shape, such as a PNG with alpha, because the app applies the same tint to remote and bundled artwork. Remote masks are rasters, so request physical pixels with `pixelWidth` and `pixelHeight` (logical size × display scale); the Expo adapter does this for template references. Template rendering needs this SDK and CLI or newer: older code generation drops the field, and older runtimes do not check it. Publishing template descriptors from the hosted console is planned; until then releases carry no `rendering` field.
|
|
106
106
|
|
|
@@ -200,7 +200,7 @@ The optional signed `animationSchemaVersion: 1` enables a per-slot `animation` d
|
|
|
200
200
|
|
|
201
201
|
Limits: UTF-8 JSON up to 512 KiB, dimensions 1–2048, at most 4,194,304 pixels, 1–60 fps, duration up to 30 seconds / 1,800 frames, 32 layers, 2,048 shapes, 4,096 keyframes/vertices, 30,000 document values and nesting depth 32. An arbitrary hash-valid JSON file is never sufficient for playback.
|
|
202
202
|
|
|
203
|
-
Verified cached animation works offline. Only the current descriptor may trigger a download; if it fails, retained historical releases may supply an already cached compatible animation. Historical bytes are reverified and never downloaded. If the current release removes animation or its placement, resolution returns `source: 'poster'` immediately instead of reviving an old animation. First-use offline and unsupported/failed animations also return that poster outcome.
|
|
203
|
+
Verified cached animation works offline. Only the current descriptor may trigger a download; if it fails, retained historical releases may supply an already cached compatible animation. Historical bytes are reverified and never downloaded. If the current release removes animation or its placement, resolution returns `source: 'poster'` immediately instead of reviving an old animation. When the placement itself is removed, `resolve()` of the same reference returns the bundled image, so the still poster is the app's bundled artwork. First-use offline and unsupported/failed animations also return that poster outcome.
|
|
204
204
|
|
|
205
205
|
## Short looping video
|
|
206
206
|
|
|
@@ -232,7 +232,7 @@ Success returns `{ source: 'remote' | 'cache', sequence, message, sha256, assetI
|
|
|
232
232
|
- Delivery is credentialless. Redirects are rejected. A streaming Fetch implementation is required so chunked responses can be bounded. The Expo adapter uses `expo/fetch` on native.
|
|
233
233
|
- Default deadline: 8 seconds per request; configurable from 20 milliseconds to 30 seconds. Clips use `videoTimeoutMs` instead (default 30 seconds, same range). Maximum manifest response: 256 KiB. Maximum image: 8 MiB. Maximum clip: 4 MiB. Maximum placements: 100.
|
|
234
234
|
- Persistent state keeps the highest accepted sequence and eight signed releases, up to 3 MiB. A lower sequence or different payload reusing a sequence is rejected. Legitimate server rollback must publish a new, higher sequence.
|
|
235
|
-
-
|
|
235
|
+
- If the current release does not publish the placement's key, the result is the bundled fallback; older releases are not used. If it publishes the key but its image cannot be used (incompatible size, rendering or state set, or a failed download or check), a compatible cached image from the retained release history is used, then the bundled fallback. Older releases never cause a new download. Eviction or more than eight accepted releases can remove a previous fallback. (Since 0.7.0-preview.1. 0.6.4-preview.1 and earlier also use a cached image from an older retained release when the current release does not publish the key.)
|
|
236
236
|
- Cache bytes are rehashed before use. The supplied adapters bound image storage to 50 MiB and 100 entries per configuration. Persistence is required before activating a new release. Corrupt replay state fails closed; it is not automatically discarded.
|
|
237
237
|
|
|
238
238
|
Clearing app data, uninstalling the app, or browser storage eviction can reset local replay protection across restarts. Changing pinned keys or the production manifest route does not reset it. An active client fails closed if accepted durable state disappears, moves backwards, conflicts, or cannot be verified. This preview does not claim protection against a compromised device or browser. Storage adapters must enforce atomic, nondecreasing state replacement. No background manifest refresh, resumable download, HTTP content negotiation, experiment assignment, or automatic signing-key distribution is implemented. Opt-in observation counts do not establish experiment exposure. Rendering support is supplied by the platform; a valid hash does not prove an image is decodable.
|
|
@@ -245,13 +245,13 @@ npm test
|
|
|
245
245
|
npm pack
|
|
246
246
|
```
|
|
247
247
|
|
|
248
|
-
Tests include the shared cross-platform contract corpus (`test/fixtures/shared`, byte-identical with the Swift and Kotlin SDKs' copies:
|
|
248
|
+
Tests include the shared cross-platform contract corpus (`test/fixtures/shared`, byte-identical with the Swift and Kotlin SDKs' copies: 149 signed manifest cases, 43 public config cases exercised as strings and objects, 18 appearance/arm resolution cases, 10 tintable icon rendering cases, 2 video resolution and 2 video byte-failure cases, 10 placement membership (history) cases, staging configuration, stateful replay, byte-failure and rendition selection cases), separate JavaScript key-set fixtures for a second trusted signer and an untrusted signer, catalog subset requests, Lottie profile rejection/metadata verification, animation cache/offline/rollback/removal behavior, clip header checks, current-only clip downloads, cache-only clip history and the clip deadline, selection, PNG/SVG bytes and offline fallback, plus real Node Ed25519 signatures and bounded mock delivery streams. They cover signature and byte tampering, independent pins, replay across client restarts, corrupt storage, offline cache fallback, contract mismatch, URL boundaries, deadlines, and offline code generation. The npm pack contains built JavaScript and declarations; it does not require TypeScript compilation when installed.
|
|
249
249
|
|
|
250
250
|
## State sets, catalog pages, and cache policies
|
|
251
251
|
|
|
252
252
|
Release 0.4.0-preview.1 adds `StateSetRef` / `resolveStateSet`, signed `loadAssetPage` / `resolveAsset` runtime handles, and `cachePolicy: 'disk' | 'memory' | 'none'`. The default remains `disk`; ephemeral image policies preserve durable signed release state. Image requests are cancellable and bounded to four concurrent transfers by default. `clearMemoryCache()` releases retained session bytes.
|
|
253
253
|
|
|
254
|
-
A state set declares 2–16 unique states in the checked-in catalog (`placements[].states`); code generation preserves those literal names. Resolve the complete family once and select its returned state. If any required member is unavailable, the whole family uses an eligible older cached release or the bundled family. Do not substitute one progress stage for another.
|
|
254
|
+
A state set declares 2–16 unique states in the checked-in catalog (`placements[].states`); code generation preserves those literal names. Resolve the complete family once and select its returned state. If any required member is unavailable, the whole family uses an eligible older cached release or the bundled family. If the current release does not publish the key, the result is the complete bundled family without consulting older releases. Do not substitute one progress stage for another.
|
|
255
255
|
|
|
256
256
|
Catalog metadata pages are signed, scoped to the app/release/request cursor, and contain at most 50 image references. They fetch no image bodies. Preserve the first page's `sequence` on continuation requests. Runtime handles are bound to their issuing client and cannot be serialized or reconstructed as trusted references. Use the Expo virtualized-list example for visible-only image resolution.
|
|
257
257
|
|
package/dist/index.js
CHANGED
|
@@ -536,6 +536,10 @@ export class AssetClient {
|
|
|
536
536
|
const resolvedOptions = { ...options, arm: decision.arm };
|
|
537
537
|
let message = this.storageFailure ?? 'No compatible published artwork is available.';
|
|
538
538
|
let reason = this.storageFailure ? 'verification' : !history.length && this.lastError ? this.lastRefreshReason : 'missing';
|
|
539
|
+
// Membership is decided by the latest accepted release alone: a key it does not
|
|
540
|
+
// publish has no artwork, so neither the cache nor an older release is consulted.
|
|
541
|
+
if (!this.storageFailure && history.length && !history[0].payload.slots.some(item => item.key === ref.key))
|
|
542
|
+
return this.observed(ref, { source: 'bundle', sequence: null, arm: null, armSource: decision.armSource, fallbackReason: 'missing', message: decisionMessage('Using bundled artwork. The current release does not publish this placement.', decision), ...(ref.bundledAccessibility ? { accessibility: ref.bundledAccessibility } : {}) });
|
|
539
543
|
for (const [index, { payload }] of history.entries()) {
|
|
540
544
|
const slot = payload.slots.find(matches);
|
|
541
545
|
if (!slot)
|
|
@@ -564,6 +568,8 @@ export class AssetClient {
|
|
|
564
568
|
throwIfAborted(options.signal);
|
|
565
569
|
const resolvedOptions = { ...options, arm: decision.arm };
|
|
566
570
|
let reason = this.storageFailure ? 'verification' : !history.length && this.lastError ? this.lastRefreshReason : 'missing';
|
|
571
|
+
if (!this.storageFailure && history.length && !history[0].payload.slots.some(item => item.key === ref.key))
|
|
572
|
+
return this.observed(ref, { source: 'bundle', sequence: null, arm: null, armSource: decision.armSource, fallbackReason: 'missing', message: decisionMessage('Using the complete bundled state set. The current release does not publish this placement.', decision), states: Object.freeze({}) });
|
|
567
573
|
for (const [index, { payload }] of history.entries()) {
|
|
568
574
|
const placement = payload.slots.find(matches);
|
|
569
575
|
const selected = placement && variantSlot(placement, decision.arm, options.appearance);
|
package/dist/telemetry.js
CHANGED
|
@@ -28,7 +28,7 @@ export class ObservationReporter {
|
|
|
28
28
|
this.fetcher = fetcher;
|
|
29
29
|
this.interval = boundedInteger(options.flushIntervalMs, 60_000, 2_147_483_647);
|
|
30
30
|
this.maxBatch = boundedInteger(options.maxBatch, 200, 200);
|
|
31
|
-
const sdk = options.sdk ?? { name: 'sdk-core', version: '0.
|
|
31
|
+
const sdk = options.sdk ?? { name: 'sdk-core', version: '0.7.0-preview.1' };
|
|
32
32
|
const build = options.build ?? { platform: 'web', appVersion: 'unknown', buildNumber: 'unknown' };
|
|
33
33
|
if (!version(sdk.name) || !version(sdk.version) || !['ios', 'android', 'web', 'expo'].includes(build.platform) || !version(build.appVersion) || !version(build.buildNumber) || (options.installId !== undefined && !validId(options.installId)))
|
|
34
34
|
throw new Error('Invalid observation configuration.');
|