@alikhalilll/a-skeleton 1.0.0 → 1.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.
package/dist/index.d.cts CHANGED
@@ -39,9 +39,12 @@ interface ASkeletonProps {
39
39
  /** When true, render the captured skeleton (or `fallback` slot) instead of the default slot. */
40
40
  loading: boolean;
41
41
  /**
42
- * Identifier used to look up + persist the captured shape. Falls back to the
43
- * slot's first child component name, then `'anonymous'`. Pass explicitly when
44
- * the same component renders different shapes depending on props.
42
+ * Identifier used to look up + persist the captured shape. Defaults to
43
+ * `"<slot-fingerprint>:<useId()>"` so every `<ASkeleton>` instance gets its
44
+ * own cache slot automatically two `<ASkeleton><UserCard/></ASkeleton>` on
45
+ * the same page never collide. Pass explicitly when you *want* multiple
46
+ * instances to share a captured shape (e.g. a list of identical cards), or
47
+ * when one instance renders different shapes depending on a prop.
45
48
  */
46
49
  cacheKey?: string;
47
50
  /** Max recursion depth during shape capture. Default 6. */
@@ -280,6 +283,7 @@ declare function useShapeProbe(getTarget: () => HTMLElement | null, options: Sha
280
283
  * `persist` is enabled. SSR-safe — bypasses `window` access on the server.
281
284
  * Rehydrates pre-computed styles for shapes deserialized from older sessions
282
285
  * (Object.freeze + style/lineStyles don't survive `JSON.stringify` round-trip).
286
+ * Drops the entry if it was written by a different schema version.
283
287
  */
284
288
  declare function getCached(key: string, persist: boolean): CachedShape | undefined;
285
289
  /** Store a captured shape. `persist=true` mirrors to `localStorage`. */
package/dist/index.d.ts CHANGED
@@ -39,9 +39,12 @@ interface ASkeletonProps {
39
39
  /** When true, render the captured skeleton (or `fallback` slot) instead of the default slot. */
40
40
  loading: boolean;
41
41
  /**
42
- * Identifier used to look up + persist the captured shape. Falls back to the
43
- * slot's first child component name, then `'anonymous'`. Pass explicitly when
44
- * the same component renders different shapes depending on props.
42
+ * Identifier used to look up + persist the captured shape. Defaults to
43
+ * `"<slot-fingerprint>:<useId()>"` so every `<ASkeleton>` instance gets its
44
+ * own cache slot automatically two `<ASkeleton><UserCard/></ASkeleton>` on
45
+ * the same page never collide. Pass explicitly when you *want* multiple
46
+ * instances to share a captured shape (e.g. a list of identical cards), or
47
+ * when one instance renders different shapes depending on a prop.
45
48
  */
46
49
  cacheKey?: string;
47
50
  /** Max recursion depth during shape capture. Default 6. */
@@ -280,6 +283,7 @@ declare function useShapeProbe(getTarget: () => HTMLElement | null, options: Sha
280
283
  * `persist` is enabled. SSR-safe — bypasses `window` access on the server.
281
284
  * Rehydrates pre-computed styles for shapes deserialized from older sessions
282
285
  * (Object.freeze + style/lineStyles don't survive `JSON.stringify` round-trip).
286
+ * Drops the entry if it was written by a different schema version.
283
287
  */
284
288
  declare function getCached(key: string, persist: boolean): CachedShape | undefined;
285
289
  /** Store a captured shape. `persist=true` mirrors to `localStorage`. */
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Comment, Fragment, Text, computed, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, h, normalizeClass, normalizeStyle, onBeforeUnmount, openBlock, ref, renderList, renderSlot, shallowRef, useSlots, watch } from "vue";
1
+ import { Comment, Fragment, Text, computed, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, h, normalizeClass, normalizeStyle, onBeforeUnmount, openBlock, ref, renderList, renderSlot, shallowRef, useId, useSlots, watch } from "vue";
2
2
  //#region ../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
3
3
  function r(e) {
4
4
  var t, f, n = "";
@@ -3652,19 +3652,32 @@ function useShapeProbe(getTarget, options) {
3652
3652
  const memory = /* @__PURE__ */ new Map();
3653
3653
  const STORAGE_PREFIX = "a-skeleton:";
3654
3654
  /**
3655
+ * Schema version for persisted entries. Bump whenever the `ShapeNode` /
3656
+ * `CachedShape` field set changes so stale localStorage payloads from older
3657
+ * releases get dropped on read instead of rehydrating into a wrong layout.
3658
+ */
3659
+ const SCHEMA_VERSION = 1;
3660
+ /**
3655
3661
  * Lookup a captured shape by key. Reads in-memory first, then `localStorage` if
3656
3662
  * `persist` is enabled. SSR-safe — bypasses `window` access on the server.
3657
3663
  * Rehydrates pre-computed styles for shapes deserialized from older sessions
3658
3664
  * (Object.freeze + style/lineStyles don't survive `JSON.stringify` round-trip).
3665
+ * Drops the entry if it was written by a different schema version.
3659
3666
  */
3660
3667
  function getCached(key, persist) {
3661
3668
  const hit = memory.get(key);
3662
3669
  if (hit) return hit;
3663
3670
  if (!persist || typeof window === "undefined") return void 0;
3664
3671
  try {
3665
- const raw = window.localStorage.getItem(STORAGE_PREFIX + key);
3672
+ const storageKey = STORAGE_PREFIX + key;
3673
+ const raw = window.localStorage.getItem(storageKey);
3666
3674
  if (!raw) return void 0;
3667
- const rehydrated = rehydrateShape(JSON.parse(raw));
3675
+ const parsed = JSON.parse(raw);
3676
+ if (parsed.v !== SCHEMA_VERSION) {
3677
+ window.localStorage.removeItem(storageKey);
3678
+ return;
3679
+ }
3680
+ const rehydrated = rehydrateShape(parsed);
3668
3681
  memory.set(key, rehydrated);
3669
3682
  return rehydrated;
3670
3683
  } catch {
@@ -3677,9 +3690,11 @@ function setCached(key, value, persist) {
3677
3690
  if (!persist || typeof window === "undefined") return;
3678
3691
  try {
3679
3692
  const lean = {
3693
+ v: SCHEMA_VERSION,
3680
3694
  width: value.width,
3681
3695
  height: value.height,
3682
- nodes: leanNodes(value.nodes)
3696
+ nodes: leanNodes(value.nodes),
3697
+ truncated: value.truncated
3683
3698
  };
3684
3699
  window.localStorage.setItem(STORAGE_PREFIX + key, JSON.stringify(lean));
3685
3700
  } catch {}
@@ -3706,7 +3721,7 @@ function clearCached(key) {
3706
3721
  * path stays allocation-free.
3707
3722
  */
3708
3723
  function rehydrateShape(shape) {
3709
- const nodes = shape.nodes.map((n) => n.style ? n : freezeNodeStyles(n));
3724
+ const nodes = shape.nodes.map((n) => freezeNodeStyles(n));
3710
3725
  return Object.freeze({
3711
3726
  nodes: Object.freeze(nodes),
3712
3727
  width: shape.width,
@@ -4131,7 +4146,9 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
4131
4146
  __expose();
4132
4147
  const props = __props;
4133
4148
  const slots = useSlots();
4134
- const resolvedKey = computed(() => props.cacheKey ?? fingerprintSlot(slots.default?.()));
4149
+ const instanceId = useId();
4150
+ const resolvedKey = computed(() => props.cacheKey ?? `${fingerprintSlot(slots.default?.())}:${instanceId}`);
4151
+ const warnedKeys = /* @__PURE__ */ new Set();
4135
4152
  const cached = shallowRef(getCached(resolvedKey.value, props.persist));
4136
4153
  watch(resolvedKey, (key) => {
4137
4154
  cached.value = getCached(key, props.persist);
@@ -4144,6 +4161,10 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
4144
4161
  onCapture: (shape) => {
4145
4162
  setCached(resolvedKey.value, shape, props.persist);
4146
4163
  cached.value = shape;
4164
+ if (shape.truncated && !warnedKeys.has(resolvedKey.value)) {
4165
+ warnedKeys.add(resolvedKey.value);
4166
+ console.warn(`[ASkeleton] Capture truncated at maxNodes=${props.maxNodes} for cacheKey="${resolvedKey.value}". The replayed skeleton will be missing nodes. Raise \`max-nodes\` or mark dense subtrees with \`data-skeleton-stop\` to collapse them into a single block.`);
4167
+ }
4147
4168
  }
4148
4169
  });
4149
4170
  const animationClass = computed(() => props.animation === "none" ? null : `a-skel-block--anim-${props.animation}`);
@@ -4165,7 +4186,9 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
4165
4186
  const __returned__ = {
4166
4187
  props,
4167
4188
  slots,
4189
+ instanceId,
4168
4190
  resolvedKey,
4191
+ warnedKeys,
4169
4192
  cached,
4170
4193
  wrapperRef,
4171
4194
  animationClass,