@electric-sql/react 0.4.0 → 0.4.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.
@@ -117,6 +117,7 @@ function parseShapeData(shape) {
117
117
  return {
118
118
  data: [...shape.valueSync.values()],
119
119
  isLoading: shape.isLoading(),
120
+ lastSyncedAt: shape.lastSyncedAt(),
120
121
  isError: shape.error !== false,
121
122
  shape,
122
123
  error: shape.error
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.ts","../../src/react-hooks.tsx"],"sourcesContent":["export * from './react-hooks'\n","import {\n Shape,\n ShapeStream,\n ShapeStreamOptions,\n Row,\n} from '@electric-sql/client'\nimport React from 'react'\nimport { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector.js'\n\nconst streamCache = new Map<string, ShapeStream>()\nconst shapeCache = new Map<ShapeStream, Shape>()\n\nexport async function preloadShape<T extends Row = Row>(\n options: ShapeStreamOptions\n): Promise<Shape<T>> {\n const shapeStream = getShapeStream<T>(options)\n const shape = getShape<T>(shapeStream)\n await shape.value\n return shape\n}\n\nexport function sortedOptionsHash(options: ShapeStreamOptions): string {\n return JSON.stringify(options, Object.keys(options).sort())\n}\n\nexport function getShapeStream<T extends Row = Row>(\n options: ShapeStreamOptions\n): ShapeStream<T> {\n const shapeHash = sortedOptionsHash(options)\n\n // If the stream is already cached, return\n if (streamCache.has(shapeHash)) {\n // Return the ShapeStream\n return streamCache.get(shapeHash)! as ShapeStream<T>\n } else {\n const newShapeStream = new ShapeStream<T>(options)\n\n streamCache.set(shapeHash, newShapeStream)\n\n // Return the created shape\n return newShapeStream\n }\n}\n\nexport function getShape<T extends Row>(shapeStream: ShapeStream<T>): Shape<T> {\n // If the stream is already cached, return\n if (shapeCache.has(shapeStream)) {\n // Return the ShapeStream\n return shapeCache.get(shapeStream)! as Shape<T>\n } else {\n const newShape = new Shape<T>(shapeStream)\n\n shapeCache.set(shapeStream, newShape)\n\n // Return the created shape\n return newShape\n }\n}\n\nexport interface UseShapeResult<T extends Row = Row> {\n /**\n * The array of rows that make up the Shape.\n * @type {T[]}\n */\n data: T[]\n /**\n * The Shape instance used by this useShape\n * @type {Shape<T>}\n */\n shape: Shape<T>\n /** True during initial fetch. False afterwise. */\n isLoading: boolean\n error: Shape<T>[`error`]\n isError: boolean\n}\n\nfunction shapeSubscribe<T extends Row>(shape: Shape<T>, callback: () => void) {\n const unsubscribe = shape.subscribe(callback)\n return () => {\n unsubscribe()\n }\n}\n\nfunction parseShapeData<T extends Row>(shape: Shape<T>): UseShapeResult<T> {\n return {\n data: [...shape.valueSync.values()],\n isLoading: shape.isLoading(),\n isError: shape.error !== false,\n shape,\n error: shape.error,\n }\n}\n\nfunction identity<T>(arg: T): T {\n return arg\n}\n\ninterface UseShapeOptions<SourceData extends Row, Selection>\n extends ShapeStreamOptions {\n selector?: (value: UseShapeResult<SourceData>) => Selection\n}\n\nexport function useShape<\n SourceData extends Row = Row,\n Selection = UseShapeResult<SourceData>,\n>({\n selector = identity as (arg: UseShapeResult<SourceData>) => Selection,\n ...options\n}: UseShapeOptions<SourceData, Selection>): Selection {\n const shapeStream = getShapeStream<SourceData>(options as ShapeStreamOptions)\n const shape = getShape<SourceData>(shapeStream)\n\n const useShapeData = React.useMemo(() => {\n let latestShapeData = parseShapeData(shape)\n const getSnapshot = () => latestShapeData\n const subscribe = (onStoreChange: () => void) =>\n shapeSubscribe(shape, () => {\n latestShapeData = parseShapeData(shape)\n onStoreChange()\n })\n\n return () => {\n return useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getSnapshot,\n selector\n )\n }\n }, [shape, selector])\n\n return useShapeData()\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAKO;AACP,mBAAkB;AAClB,2BAAiD;AAEjD,IAAM,cAAc,oBAAI,IAAyB;AACjD,IAAM,aAAa,oBAAI,IAAwB;AAE/C,SAAsB,aACpB,SACmB;AAAA;AACnB,UAAM,cAAc,eAAkB,OAAO;AAC7C,UAAM,QAAQ,SAAY,WAAW;AACrC,UAAM,MAAM;AACZ,WAAO;AAAA,EACT;AAAA;AAEO,SAAS,kBAAkB,SAAqC;AACrE,SAAO,KAAK,UAAU,SAAS,OAAO,KAAK,OAAO,EAAE,KAAK,CAAC;AAC5D;AAEO,SAAS,eACd,SACgB;AAChB,QAAM,YAAY,kBAAkB,OAAO;AAG3C,MAAI,YAAY,IAAI,SAAS,GAAG;AAE9B,WAAO,YAAY,IAAI,SAAS;AAAA,EAClC,OAAO;AACL,UAAM,iBAAiB,IAAI,0BAAe,OAAO;AAEjD,gBAAY,IAAI,WAAW,cAAc;AAGzC,WAAO;AAAA,EACT;AACF;AAEO,SAAS,SAAwB,aAAuC;AAE7E,MAAI,WAAW,IAAI,WAAW,GAAG;AAE/B,WAAO,WAAW,IAAI,WAAW;AAAA,EACnC,OAAO;AACL,UAAM,WAAW,IAAI,oBAAS,WAAW;AAEzC,eAAW,IAAI,aAAa,QAAQ;AAGpC,WAAO;AAAA,EACT;AACF;AAmBA,SAAS,eAA8B,OAAiB,UAAsB;AAC5E,QAAM,cAAc,MAAM,UAAU,QAAQ;AAC5C,SAAO,MAAM;AACX,gBAAY;AAAA,EACd;AACF;AAEA,SAAS,eAA8B,OAAoC;AACzE,SAAO;AAAA,IACL,MAAM,CAAC,GAAG,MAAM,UAAU,OAAO,CAAC;AAAA,IAClC,WAAW,MAAM,UAAU;AAAA,IAC3B,SAAS,MAAM,UAAU;AAAA,IACzB;AAAA,IACA,OAAO,MAAM;AAAA,EACf;AACF;AAEA,SAAS,SAAY,KAAW;AAC9B,SAAO;AACT;AAOO,SAAS,SAGd,IAGoD;AAHpD,eACA;AAAA,eAAW;AAAA,EA1Gb,IAyGE,IAEG,oBAFH,IAEG;AAAA,IADH;AAAA;AAGA,QAAM,cAAc,eAA2B,OAA6B;AAC5E,QAAM,QAAQ,SAAqB,WAAW;AAE9C,QAAM,eAAe,aAAAA,QAAM,QAAQ,MAAM;AACvC,QAAI,kBAAkB,eAAe,KAAK;AAC1C,UAAM,cAAc,MAAM;AAC1B,UAAM,YAAY,CAAC,kBACjB,eAAe,OAAO,MAAM;AAC1B,wBAAkB,eAAe,KAAK;AACtC,oBAAc;AAAA,IAChB,CAAC;AAEH,WAAO,MAAM;AACX,iBAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,QAAQ,CAAC;AAEpB,SAAO,aAAa;AACtB;","names":["React"]}
1
+ {"version":3,"sources":["../../src/index.ts","../../src/react-hooks.tsx"],"sourcesContent":["export * from './react-hooks'\n","import {\n Shape,\n ShapeStream,\n ShapeStreamOptions,\n Row,\n} from '@electric-sql/client'\nimport React from 'react'\nimport { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector.js'\n\nconst streamCache = new Map<string, ShapeStream>()\nconst shapeCache = new Map<ShapeStream, Shape>()\n\nexport async function preloadShape<T extends Row = Row>(\n options: ShapeStreamOptions\n): Promise<Shape<T>> {\n const shapeStream = getShapeStream<T>(options)\n const shape = getShape<T>(shapeStream)\n await shape.value\n return shape\n}\n\nexport function sortedOptionsHash(options: ShapeStreamOptions): string {\n return JSON.stringify(options, Object.keys(options).sort())\n}\n\nexport function getShapeStream<T extends Row = Row>(\n options: ShapeStreamOptions\n): ShapeStream<T> {\n const shapeHash = sortedOptionsHash(options)\n\n // If the stream is already cached, return\n if (streamCache.has(shapeHash)) {\n // Return the ShapeStream\n return streamCache.get(shapeHash)! as ShapeStream<T>\n } else {\n const newShapeStream = new ShapeStream<T>(options)\n\n streamCache.set(shapeHash, newShapeStream)\n\n // Return the created shape\n return newShapeStream\n }\n}\n\nexport function getShape<T extends Row>(shapeStream: ShapeStream<T>): Shape<T> {\n // If the stream is already cached, return\n if (shapeCache.has(shapeStream)) {\n // Return the ShapeStream\n return shapeCache.get(shapeStream)! as Shape<T>\n } else {\n const newShape = new Shape<T>(shapeStream)\n\n shapeCache.set(shapeStream, newShape)\n\n // Return the created shape\n return newShape\n }\n}\n\nexport interface UseShapeResult<T extends Row = Row> {\n /**\n * The array of rows that make up the Shape.\n * @type {T[]}\n */\n data: T[]\n /**\n * The Shape instance used by this useShape\n * @type {Shape<T>}\n */\n shape: Shape<T>\n /** True during initial fetch. False afterwise. */\n isLoading: boolean\n /** Unix time at which we last synced. Undefined when `isLoading` is true. */\n lastSyncedAt?: number\n error: Shape<T>[`error`]\n isError: boolean\n}\n\nfunction shapeSubscribe<T extends Row>(shape: Shape<T>, callback: () => void) {\n const unsubscribe = shape.subscribe(callback)\n return () => {\n unsubscribe()\n }\n}\n\nfunction parseShapeData<T extends Row>(shape: Shape<T>): UseShapeResult<T> {\n return {\n data: [...shape.valueSync.values()],\n isLoading: shape.isLoading(),\n lastSyncedAt: shape.lastSyncedAt(),\n isError: shape.error !== false,\n shape,\n error: shape.error,\n }\n}\n\nfunction identity<T>(arg: T): T {\n return arg\n}\n\ninterface UseShapeOptions<SourceData extends Row, Selection>\n extends ShapeStreamOptions {\n selector?: (value: UseShapeResult<SourceData>) => Selection\n}\n\nexport function useShape<\n SourceData extends Row = Row,\n Selection = UseShapeResult<SourceData>,\n>({\n selector = identity as (arg: UseShapeResult<SourceData>) => Selection,\n ...options\n}: UseShapeOptions<SourceData, Selection>): Selection {\n const shapeStream = getShapeStream<SourceData>(options as ShapeStreamOptions)\n const shape = getShape<SourceData>(shapeStream)\n\n const useShapeData = React.useMemo(() => {\n let latestShapeData = parseShapeData(shape)\n const getSnapshot = () => latestShapeData\n const subscribe = (onStoreChange: () => void) =>\n shapeSubscribe(shape, () => {\n latestShapeData = parseShapeData(shape)\n onStoreChange()\n })\n\n return () => {\n return useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getSnapshot,\n selector\n )\n }\n }, [shape, selector])\n\n return useShapeData()\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAKO;AACP,mBAAkB;AAClB,2BAAiD;AAEjD,IAAM,cAAc,oBAAI,IAAyB;AACjD,IAAM,aAAa,oBAAI,IAAwB;AAE/C,SAAsB,aACpB,SACmB;AAAA;AACnB,UAAM,cAAc,eAAkB,OAAO;AAC7C,UAAM,QAAQ,SAAY,WAAW;AACrC,UAAM,MAAM;AACZ,WAAO;AAAA,EACT;AAAA;AAEO,SAAS,kBAAkB,SAAqC;AACrE,SAAO,KAAK,UAAU,SAAS,OAAO,KAAK,OAAO,EAAE,KAAK,CAAC;AAC5D;AAEO,SAAS,eACd,SACgB;AAChB,QAAM,YAAY,kBAAkB,OAAO;AAG3C,MAAI,YAAY,IAAI,SAAS,GAAG;AAE9B,WAAO,YAAY,IAAI,SAAS;AAAA,EAClC,OAAO;AACL,UAAM,iBAAiB,IAAI,0BAAe,OAAO;AAEjD,gBAAY,IAAI,WAAW,cAAc;AAGzC,WAAO;AAAA,EACT;AACF;AAEO,SAAS,SAAwB,aAAuC;AAE7E,MAAI,WAAW,IAAI,WAAW,GAAG;AAE/B,WAAO,WAAW,IAAI,WAAW;AAAA,EACnC,OAAO;AACL,UAAM,WAAW,IAAI,oBAAS,WAAW;AAEzC,eAAW,IAAI,aAAa,QAAQ;AAGpC,WAAO;AAAA,EACT;AACF;AAqBA,SAAS,eAA8B,OAAiB,UAAsB;AAC5E,QAAM,cAAc,MAAM,UAAU,QAAQ;AAC5C,SAAO,MAAM;AACX,gBAAY;AAAA,EACd;AACF;AAEA,SAAS,eAA8B,OAAoC;AACzE,SAAO;AAAA,IACL,MAAM,CAAC,GAAG,MAAM,UAAU,OAAO,CAAC;AAAA,IAClC,WAAW,MAAM,UAAU;AAAA,IAC3B,cAAc,MAAM,aAAa;AAAA,IACjC,SAAS,MAAM,UAAU;AAAA,IACzB;AAAA,IACA,OAAO,MAAM;AAAA,EACf;AACF;AAEA,SAAS,SAAY,KAAW;AAC9B,SAAO;AACT;AAOO,SAAS,SAGd,IAGoD;AAHpD,eACA;AAAA,eAAW;AAAA,EA7Gb,IA4GE,IAEG,oBAFH,IAEG;AAAA,IADH;AAAA;AAGA,QAAM,cAAc,eAA2B,OAA6B;AAC5E,QAAM,QAAQ,SAAqB,WAAW;AAE9C,QAAM,eAAe,aAAAA,QAAM,QAAQ,MAAM;AACvC,QAAI,kBAAkB,eAAe,KAAK;AAC1C,UAAM,cAAc,MAAM;AAC1B,UAAM,YAAY,CAAC,kBACjB,eAAe,OAAO,MAAM;AAC1B,wBAAkB,eAAe,KAAK;AACtC,oBAAc;AAAA,IAChB,CAAC;AAEH,WAAO,MAAM;AACX,iBAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,QAAQ,CAAC;AAEpB,SAAO,aAAa;AACtB;","names":["React"]}
@@ -1,2 +1,2 @@
1
- var u=Object.getOwnPropertySymbols;var f=Object.prototype.hasOwnProperty,x=Object.prototype.propertyIsEnumerable;var l=(e,t)=>{var a={};for(var r in e)f.call(e,r)&&t.indexOf(r)<0&&(a[r]=e[r]);if(e!=null&&u)for(var r of u(e))t.indexOf(r)<0&&x.call(e,r)&&(a[r]=e[r]);return a};var T=(e,t,a)=>new Promise((r,p)=>{var n=o=>{try{s(a.next(o))}catch(S){p(S)}},h=o=>{try{s(a.throw(o))}catch(S){p(S)}},s=o=>o.done?r(o.value):Promise.resolve(o.value).then(n,h);s((a=a.apply(e,t)).next())});import{Shape as g,ShapeStream as D}from"@electric-sql/client";import b from"react";import{useSyncExternalStoreWithSelector as O}from"use-sync-external-store/with-selector.js";var i=new Map,c=new Map;function k(e){return T(this,null,function*(){let t=w(e),a=d(t);return yield a.value,a})}function U(e){return JSON.stringify(e,Object.keys(e).sort())}function w(e){let t=U(e);if(i.has(t))return i.get(t);{let a=new D(e);return i.set(t,a),a}}function d(e){if(c.has(e))return c.get(e);{let t=new g(e);return c.set(e,t),t}}function v(e,t){let a=e.subscribe(t);return()=>{a()}}function m(e){return{data:[...e.valueSync.values()],isLoading:e.isLoading(),isError:e.error!==!1,shape:e,error:e.error}}function y(e){return e}function J(a){var r=a,{selector:e=y}=r,t=l(r,["selector"]);let p=w(t),n=d(p);return b.useMemo(()=>{let s=m(n),o=()=>s,S=R=>v(n,()=>{s=m(n),R()});return()=>O(S,o,o,e)},[n,e])()}export{d as getShape,w as getShapeStream,k as preloadShape,U as sortedOptionsHash,J as useShape};
1
+ var h=Object.getOwnPropertySymbols;var f=Object.prototype.hasOwnProperty,x=Object.prototype.propertyIsEnumerable;var l=(e,t)=>{var a={};for(var r in e)f.call(e,r)&&t.indexOf(r)<0&&(a[r]=e[r]);if(e!=null&&h)for(var r of h(e))t.indexOf(r)<0&&x.call(e,r)&&(a[r]=e[r]);return a};var T=(e,t,a)=>new Promise((r,p)=>{var n=o=>{try{s(a.next(o))}catch(S){p(S)}},u=o=>{try{s(a.throw(o))}catch(S){p(S)}},s=o=>o.done?r(o.value):Promise.resolve(o.value).then(n,u);s((a=a.apply(e,t)).next())});import{Shape as b,ShapeStream as g}from"@electric-sql/client";import D from"react";import{useSyncExternalStoreWithSelector as O}from"use-sync-external-store/with-selector.js";var c=new Map,i=new Map;function j(e){return T(this,null,function*(){let t=d(e),a=w(t);return yield a.value,a})}function y(e){return JSON.stringify(e,Object.keys(e).sort())}function d(e){let t=y(e);if(c.has(t))return c.get(t);{let a=new g(e);return c.set(t,a),a}}function w(e){if(i.has(e))return i.get(e);{let t=new b(e);return i.set(e,t),t}}function U(e,t){let a=e.subscribe(t);return()=>{a()}}function m(e){return{data:[...e.valueSync.values()],isLoading:e.isLoading(),lastSyncedAt:e.lastSyncedAt(),isError:e.error!==!1,shape:e,error:e.error}}function v(e){return e}function k(a){var r=a,{selector:e=v}=r,t=l(r,["selector"]);let p=d(t),n=w(p);return D.useMemo(()=>{let s=m(n),o=()=>s,S=R=>U(n,()=>{s=m(n),R()});return()=>O(S,o,o,e)},[n,e])()}export{w as getShape,d as getShapeStream,j as preloadShape,y as sortedOptionsHash,k as useShape};
2
2
  //# sourceMappingURL=index.browser.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react-hooks.tsx"],"sourcesContent":["import {\n Shape,\n ShapeStream,\n ShapeStreamOptions,\n Row,\n} from '@electric-sql/client'\nimport React from 'react'\nimport { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector.js'\n\nconst streamCache = new Map<string, ShapeStream>()\nconst shapeCache = new Map<ShapeStream, Shape>()\n\nexport async function preloadShape<T extends Row = Row>(\n options: ShapeStreamOptions\n): Promise<Shape<T>> {\n const shapeStream = getShapeStream<T>(options)\n const shape = getShape<T>(shapeStream)\n await shape.value\n return shape\n}\n\nexport function sortedOptionsHash(options: ShapeStreamOptions): string {\n return JSON.stringify(options, Object.keys(options).sort())\n}\n\nexport function getShapeStream<T extends Row = Row>(\n options: ShapeStreamOptions\n): ShapeStream<T> {\n const shapeHash = sortedOptionsHash(options)\n\n // If the stream is already cached, return\n if (streamCache.has(shapeHash)) {\n // Return the ShapeStream\n return streamCache.get(shapeHash)! as ShapeStream<T>\n } else {\n const newShapeStream = new ShapeStream<T>(options)\n\n streamCache.set(shapeHash, newShapeStream)\n\n // Return the created shape\n return newShapeStream\n }\n}\n\nexport function getShape<T extends Row>(shapeStream: ShapeStream<T>): Shape<T> {\n // If the stream is already cached, return\n if (shapeCache.has(shapeStream)) {\n // Return the ShapeStream\n return shapeCache.get(shapeStream)! as Shape<T>\n } else {\n const newShape = new Shape<T>(shapeStream)\n\n shapeCache.set(shapeStream, newShape)\n\n // Return the created shape\n return newShape\n }\n}\n\nexport interface UseShapeResult<T extends Row = Row> {\n /**\n * The array of rows that make up the Shape.\n * @type {T[]}\n */\n data: T[]\n /**\n * The Shape instance used by this useShape\n * @type {Shape<T>}\n */\n shape: Shape<T>\n /** True during initial fetch. False afterwise. */\n isLoading: boolean\n error: Shape<T>[`error`]\n isError: boolean\n}\n\nfunction shapeSubscribe<T extends Row>(shape: Shape<T>, callback: () => void) {\n const unsubscribe = shape.subscribe(callback)\n return () => {\n unsubscribe()\n }\n}\n\nfunction parseShapeData<T extends Row>(shape: Shape<T>): UseShapeResult<T> {\n return {\n data: [...shape.valueSync.values()],\n isLoading: shape.isLoading(),\n isError: shape.error !== false,\n shape,\n error: shape.error,\n }\n}\n\nfunction identity<T>(arg: T): T {\n return arg\n}\n\ninterface UseShapeOptions<SourceData extends Row, Selection>\n extends ShapeStreamOptions {\n selector?: (value: UseShapeResult<SourceData>) => Selection\n}\n\nexport function useShape<\n SourceData extends Row = Row,\n Selection = UseShapeResult<SourceData>,\n>({\n selector = identity as (arg: UseShapeResult<SourceData>) => Selection,\n ...options\n}: UseShapeOptions<SourceData, Selection>): Selection {\n const shapeStream = getShapeStream<SourceData>(options as ShapeStreamOptions)\n const shape = getShape<SourceData>(shapeStream)\n\n const useShapeData = React.useMemo(() => {\n let latestShapeData = parseShapeData(shape)\n const getSnapshot = () => latestShapeData\n const subscribe = (onStoreChange: () => void) =>\n shapeSubscribe(shape, () => {\n latestShapeData = parseShapeData(shape)\n onStoreChange()\n })\n\n return () => {\n return useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getSnapshot,\n selector\n )\n }\n }, [shape, selector])\n\n return useShapeData()\n}\n"],"mappings":"geAAA,OACE,SAAAA,EACA,eAAAC,MAGK,uBACP,OAAOC,MAAW,QAClB,OAAS,oCAAAC,MAAwC,2CAEjD,IAAMC,EAAc,IAAI,IAClBC,EAAa,IAAI,IAEvB,SAAsBC,EACpBC,EACmB,QAAAC,EAAA,sBACnB,IAAMC,EAAcC,EAAkBH,CAAO,EACvCI,EAAQC,EAAYH,CAAW,EACrC,aAAME,EAAM,MACLA,CACT,GAEO,SAASE,EAAkBN,EAAqC,CACrE,OAAO,KAAK,UAAUA,EAAS,OAAO,KAAKA,CAAO,EAAE,KAAK,CAAC,CAC5D,CAEO,SAASG,EACdH,EACgB,CAChB,IAAMO,EAAYD,EAAkBN,CAAO,EAG3C,GAAIH,EAAY,IAAIU,CAAS,EAE3B,OAAOV,EAAY,IAAIU,CAAS,EAC3B,CACL,IAAMC,EAAiB,IAAIC,EAAeT,CAAO,EAEjD,OAAAH,EAAY,IAAIU,EAAWC,CAAc,EAGlCA,CACT,CACF,CAEO,SAASH,EAAwBH,EAAuC,CAE7E,GAAIJ,EAAW,IAAII,CAAW,EAE5B,OAAOJ,EAAW,IAAII,CAAW,EAC5B,CACL,IAAMQ,EAAW,IAAIC,EAAST,CAAW,EAEzC,OAAAJ,EAAW,IAAII,EAAaQ,CAAQ,EAG7BA,CACT,CACF,CAmBA,SAASE,EAA8BR,EAAiBS,EAAsB,CAC5E,IAAMC,EAAcV,EAAM,UAAUS,CAAQ,EAC5C,MAAO,IAAM,CACXC,EAAY,CACd,CACF,CAEA,SAASC,EAA8BX,EAAoC,CACzE,MAAO,CACL,KAAM,CAAC,GAAGA,EAAM,UAAU,OAAO,CAAC,EAClC,UAAWA,EAAM,UAAU,EAC3B,QAASA,EAAM,QAAU,GACzB,MAAAA,EACA,MAAOA,EAAM,KACf,CACF,CAEA,SAASY,EAAYC,EAAW,CAC9B,OAAOA,CACT,CAOO,SAASC,EAGdC,EAGoD,CAHpD,IAAAC,EAAAD,EACA,UAAAE,EAAWL,CA1Gb,EAyGEI,EAEGpB,EAAAsB,EAFHF,EAEG,CADH,aAGA,IAAMlB,EAAcC,EAA2BH,CAA6B,EACtEI,EAAQC,EAAqBH,CAAW,EAqB9C,OAnBqBqB,EAAM,QAAQ,IAAM,CACvC,IAAIC,EAAkBT,EAAeX,CAAK,EACpCqB,EAAc,IAAMD,EACpBE,EAAaC,GACjBf,EAAeR,EAAO,IAAM,CAC1BoB,EAAkBT,EAAeX,CAAK,EACtCuB,EAAc,CAChB,CAAC,EAEH,MAAO,IACEC,EACLF,EACAD,EACAA,EACAJ,CACF,CAEJ,EAAG,CAACjB,EAAOiB,CAAQ,CAAC,EAEA,CACtB","names":["Shape","ShapeStream","React","useSyncExternalStoreWithSelector","streamCache","shapeCache","preloadShape","options","__async","shapeStream","getShapeStream","shape","getShape","sortedOptionsHash","shapeHash","newShapeStream","ShapeStream","newShape","Shape","shapeSubscribe","callback","unsubscribe","parseShapeData","identity","arg","useShape","_a","_b","selector","__objRest","React","latestShapeData","getSnapshot","subscribe","onStoreChange","useSyncExternalStoreWithSelector"]}
1
+ {"version":3,"sources":["../src/react-hooks.tsx"],"sourcesContent":["import {\n Shape,\n ShapeStream,\n ShapeStreamOptions,\n Row,\n} from '@electric-sql/client'\nimport React from 'react'\nimport { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector.js'\n\nconst streamCache = new Map<string, ShapeStream>()\nconst shapeCache = new Map<ShapeStream, Shape>()\n\nexport async function preloadShape<T extends Row = Row>(\n options: ShapeStreamOptions\n): Promise<Shape<T>> {\n const shapeStream = getShapeStream<T>(options)\n const shape = getShape<T>(shapeStream)\n await shape.value\n return shape\n}\n\nexport function sortedOptionsHash(options: ShapeStreamOptions): string {\n return JSON.stringify(options, Object.keys(options).sort())\n}\n\nexport function getShapeStream<T extends Row = Row>(\n options: ShapeStreamOptions\n): ShapeStream<T> {\n const shapeHash = sortedOptionsHash(options)\n\n // If the stream is already cached, return\n if (streamCache.has(shapeHash)) {\n // Return the ShapeStream\n return streamCache.get(shapeHash)! as ShapeStream<T>\n } else {\n const newShapeStream = new ShapeStream<T>(options)\n\n streamCache.set(shapeHash, newShapeStream)\n\n // Return the created shape\n return newShapeStream\n }\n}\n\nexport function getShape<T extends Row>(shapeStream: ShapeStream<T>): Shape<T> {\n // If the stream is already cached, return\n if (shapeCache.has(shapeStream)) {\n // Return the ShapeStream\n return shapeCache.get(shapeStream)! as Shape<T>\n } else {\n const newShape = new Shape<T>(shapeStream)\n\n shapeCache.set(shapeStream, newShape)\n\n // Return the created shape\n return newShape\n }\n}\n\nexport interface UseShapeResult<T extends Row = Row> {\n /**\n * The array of rows that make up the Shape.\n * @type {T[]}\n */\n data: T[]\n /**\n * The Shape instance used by this useShape\n * @type {Shape<T>}\n */\n shape: Shape<T>\n /** True during initial fetch. False afterwise. */\n isLoading: boolean\n /** Unix time at which we last synced. Undefined when `isLoading` is true. */\n lastSyncedAt?: number\n error: Shape<T>[`error`]\n isError: boolean\n}\n\nfunction shapeSubscribe<T extends Row>(shape: Shape<T>, callback: () => void) {\n const unsubscribe = shape.subscribe(callback)\n return () => {\n unsubscribe()\n }\n}\n\nfunction parseShapeData<T extends Row>(shape: Shape<T>): UseShapeResult<T> {\n return {\n data: [...shape.valueSync.values()],\n isLoading: shape.isLoading(),\n lastSyncedAt: shape.lastSyncedAt(),\n isError: shape.error !== false,\n shape,\n error: shape.error,\n }\n}\n\nfunction identity<T>(arg: T): T {\n return arg\n}\n\ninterface UseShapeOptions<SourceData extends Row, Selection>\n extends ShapeStreamOptions {\n selector?: (value: UseShapeResult<SourceData>) => Selection\n}\n\nexport function useShape<\n SourceData extends Row = Row,\n Selection = UseShapeResult<SourceData>,\n>({\n selector = identity as (arg: UseShapeResult<SourceData>) => Selection,\n ...options\n}: UseShapeOptions<SourceData, Selection>): Selection {\n const shapeStream = getShapeStream<SourceData>(options as ShapeStreamOptions)\n const shape = getShape<SourceData>(shapeStream)\n\n const useShapeData = React.useMemo(() => {\n let latestShapeData = parseShapeData(shape)\n const getSnapshot = () => latestShapeData\n const subscribe = (onStoreChange: () => void) =>\n shapeSubscribe(shape, () => {\n latestShapeData = parseShapeData(shape)\n onStoreChange()\n })\n\n return () => {\n return useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getSnapshot,\n selector\n )\n }\n }, [shape, selector])\n\n return useShapeData()\n}\n"],"mappings":"geAAA,OACE,SAAAA,EACA,eAAAC,MAGK,uBACP,OAAOC,MAAW,QAClB,OAAS,oCAAAC,MAAwC,2CAEjD,IAAMC,EAAc,IAAI,IAClBC,EAAa,IAAI,IAEvB,SAAsBC,EACpBC,EACmB,QAAAC,EAAA,sBACnB,IAAMC,EAAcC,EAAkBH,CAAO,EACvCI,EAAQC,EAAYH,CAAW,EACrC,aAAME,EAAM,MACLA,CACT,GAEO,SAASE,EAAkBN,EAAqC,CACrE,OAAO,KAAK,UAAUA,EAAS,OAAO,KAAKA,CAAO,EAAE,KAAK,CAAC,CAC5D,CAEO,SAASG,EACdH,EACgB,CAChB,IAAMO,EAAYD,EAAkBN,CAAO,EAG3C,GAAIH,EAAY,IAAIU,CAAS,EAE3B,OAAOV,EAAY,IAAIU,CAAS,EAC3B,CACL,IAAMC,EAAiB,IAAIC,EAAeT,CAAO,EAEjD,OAAAH,EAAY,IAAIU,EAAWC,CAAc,EAGlCA,CACT,CACF,CAEO,SAASH,EAAwBH,EAAuC,CAE7E,GAAIJ,EAAW,IAAII,CAAW,EAE5B,OAAOJ,EAAW,IAAII,CAAW,EAC5B,CACL,IAAMQ,EAAW,IAAIC,EAAST,CAAW,EAEzC,OAAAJ,EAAW,IAAII,EAAaQ,CAAQ,EAG7BA,CACT,CACF,CAqBA,SAASE,EAA8BR,EAAiBS,EAAsB,CAC5E,IAAMC,EAAcV,EAAM,UAAUS,CAAQ,EAC5C,MAAO,IAAM,CACXC,EAAY,CACd,CACF,CAEA,SAASC,EAA8BX,EAAoC,CACzE,MAAO,CACL,KAAM,CAAC,GAAGA,EAAM,UAAU,OAAO,CAAC,EAClC,UAAWA,EAAM,UAAU,EAC3B,aAAcA,EAAM,aAAa,EACjC,QAASA,EAAM,QAAU,GACzB,MAAAA,EACA,MAAOA,EAAM,KACf,CACF,CAEA,SAASY,EAAYC,EAAW,CAC9B,OAAOA,CACT,CAOO,SAASC,EAGdC,EAGoD,CAHpD,IAAAC,EAAAD,EACA,UAAAE,EAAWL,CA7Gb,EA4GEI,EAEGpB,EAAAsB,EAFHF,EAEG,CADH,aAGA,IAAMlB,EAAcC,EAA2BH,CAA6B,EACtEI,EAAQC,EAAqBH,CAAW,EAqB9C,OAnBqBqB,EAAM,QAAQ,IAAM,CACvC,IAAIC,EAAkBT,EAAeX,CAAK,EACpCqB,EAAc,IAAMD,EACpBE,EAAaC,GACjBf,EAAeR,EAAO,IAAM,CAC1BoB,EAAkBT,EAAeX,CAAK,EACtCuB,EAAc,CAChB,CAAC,EAEH,MAAO,IACEC,EACLF,EACAD,EACAA,EACAJ,CACF,CAEJ,EAAG,CAACjB,EAAOiB,CAAQ,CAAC,EAEA,CACtB","names":["Shape","ShapeStream","React","useSyncExternalStoreWithSelector","streamCache","shapeCache","preloadShape","options","__async","shapeStream","getShapeStream","shape","getShape","sortedOptionsHash","shapeHash","newShapeStream","ShapeStream","newShape","Shape","shapeSubscribe","callback","unsubscribe","parseShapeData","identity","arg","useShape","_a","_b","selector","__objRest","React","latestShapeData","getSnapshot","subscribe","onStoreChange","useSyncExternalStoreWithSelector"]}
package/dist/index.d.ts CHANGED
@@ -17,6 +17,8 @@ interface UseShapeResult<T extends Row = Row> {
17
17
  shape: Shape<T>;
18
18
  /** True during initial fetch. False afterwise. */
19
19
  isLoading: boolean;
20
+ /** Unix time at which we last synced. Undefined when `isLoading` is true. */
21
+ lastSyncedAt?: number;
20
22
  error: Shape<T>[`error`];
21
23
  isError: boolean;
22
24
  }
@@ -61,6 +61,7 @@ function parseShapeData(shape) {
61
61
  return {
62
62
  data: [...shape.valueSync.values()],
63
63
  isLoading: shape.isLoading(),
64
+ lastSyncedAt: shape.lastSyncedAt(),
64
65
  isError: shape.error !== false,
65
66
  shape,
66
67
  error: shape.error
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react-hooks.tsx"],"sourcesContent":["import {\n Shape,\n ShapeStream,\n ShapeStreamOptions,\n Row,\n} from '@electric-sql/client'\nimport React from 'react'\nimport { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector.js'\n\nconst streamCache = new Map<string, ShapeStream>()\nconst shapeCache = new Map<ShapeStream, Shape>()\n\nexport async function preloadShape<T extends Row = Row>(\n options: ShapeStreamOptions\n): Promise<Shape<T>> {\n const shapeStream = getShapeStream<T>(options)\n const shape = getShape<T>(shapeStream)\n await shape.value\n return shape\n}\n\nexport function sortedOptionsHash(options: ShapeStreamOptions): string {\n return JSON.stringify(options, Object.keys(options).sort())\n}\n\nexport function getShapeStream<T extends Row = Row>(\n options: ShapeStreamOptions\n): ShapeStream<T> {\n const shapeHash = sortedOptionsHash(options)\n\n // If the stream is already cached, return\n if (streamCache.has(shapeHash)) {\n // Return the ShapeStream\n return streamCache.get(shapeHash)! as ShapeStream<T>\n } else {\n const newShapeStream = new ShapeStream<T>(options)\n\n streamCache.set(shapeHash, newShapeStream)\n\n // Return the created shape\n return newShapeStream\n }\n}\n\nexport function getShape<T extends Row>(shapeStream: ShapeStream<T>): Shape<T> {\n // If the stream is already cached, return\n if (shapeCache.has(shapeStream)) {\n // Return the ShapeStream\n return shapeCache.get(shapeStream)! as Shape<T>\n } else {\n const newShape = new Shape<T>(shapeStream)\n\n shapeCache.set(shapeStream, newShape)\n\n // Return the created shape\n return newShape\n }\n}\n\nexport interface UseShapeResult<T extends Row = Row> {\n /**\n * The array of rows that make up the Shape.\n * @type {T[]}\n */\n data: T[]\n /**\n * The Shape instance used by this useShape\n * @type {Shape<T>}\n */\n shape: Shape<T>\n /** True during initial fetch. False afterwise. */\n isLoading: boolean\n error: Shape<T>[`error`]\n isError: boolean\n}\n\nfunction shapeSubscribe<T extends Row>(shape: Shape<T>, callback: () => void) {\n const unsubscribe = shape.subscribe(callback)\n return () => {\n unsubscribe()\n }\n}\n\nfunction parseShapeData<T extends Row>(shape: Shape<T>): UseShapeResult<T> {\n return {\n data: [...shape.valueSync.values()],\n isLoading: shape.isLoading(),\n isError: shape.error !== false,\n shape,\n error: shape.error,\n }\n}\n\nfunction identity<T>(arg: T): T {\n return arg\n}\n\ninterface UseShapeOptions<SourceData extends Row, Selection>\n extends ShapeStreamOptions {\n selector?: (value: UseShapeResult<SourceData>) => Selection\n}\n\nexport function useShape<\n SourceData extends Row = Row,\n Selection = UseShapeResult<SourceData>,\n>({\n selector = identity as (arg: UseShapeResult<SourceData>) => Selection,\n ...options\n}: UseShapeOptions<SourceData, Selection>): Selection {\n const shapeStream = getShapeStream<SourceData>(options as ShapeStreamOptions)\n const shape = getShape<SourceData>(shapeStream)\n\n const useShapeData = React.useMemo(() => {\n let latestShapeData = parseShapeData(shape)\n const getSnapshot = () => latestShapeData\n const subscribe = (onStoreChange: () => void) =>\n shapeSubscribe(shape, () => {\n latestShapeData = parseShapeData(shape)\n onStoreChange()\n })\n\n return () => {\n return useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getSnapshot,\n selector\n )\n }\n }, [shape, selector])\n\n return useShapeData()\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAAA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP,OAAO,WAAW;AAClB,SAAS,wCAAwC;AAEjD,IAAM,cAAc,oBAAI,IAAyB;AACjD,IAAM,aAAa,oBAAI,IAAwB;AAE/C,eAAsB,aACpB,SACmB;AACnB,QAAM,cAAc,eAAkB,OAAO;AAC7C,QAAM,QAAQ,SAAY,WAAW;AACrC,QAAM,MAAM;AACZ,SAAO;AACT;AAEO,SAAS,kBAAkB,SAAqC;AACrE,SAAO,KAAK,UAAU,SAAS,OAAO,KAAK,OAAO,EAAE,KAAK,CAAC;AAC5D;AAEO,SAAS,eACd,SACgB;AAChB,QAAM,YAAY,kBAAkB,OAAO;AAG3C,MAAI,YAAY,IAAI,SAAS,GAAG;AAE9B,WAAO,YAAY,IAAI,SAAS;AAAA,EAClC,OAAO;AACL,UAAM,iBAAiB,IAAI,YAAe,OAAO;AAEjD,gBAAY,IAAI,WAAW,cAAc;AAGzC,WAAO;AAAA,EACT;AACF;AAEO,SAAS,SAAwB,aAAuC;AAE7E,MAAI,WAAW,IAAI,WAAW,GAAG;AAE/B,WAAO,WAAW,IAAI,WAAW;AAAA,EACnC,OAAO;AACL,UAAM,WAAW,IAAI,MAAS,WAAW;AAEzC,eAAW,IAAI,aAAa,QAAQ;AAGpC,WAAO;AAAA,EACT;AACF;AAmBA,SAAS,eAA8B,OAAiB,UAAsB;AAC5E,QAAM,cAAc,MAAM,UAAU,QAAQ;AAC5C,SAAO,MAAM;AACX,gBAAY;AAAA,EACd;AACF;AAEA,SAAS,eAA8B,OAAoC;AACzE,SAAO;AAAA,IACL,MAAM,CAAC,GAAG,MAAM,UAAU,OAAO,CAAC;AAAA,IAClC,WAAW,MAAM,UAAU;AAAA,IAC3B,SAAS,MAAM,UAAU;AAAA,IACzB;AAAA,IACA,OAAO,MAAM;AAAA,EACf;AACF;AAEA,SAAS,SAAY,KAAW;AAC9B,SAAO;AACT;AAOO,SAAS,SAGd,IAGoD;AAHpD,eACA;AAAA,eAAW;AAAA,EA1Gb,IAyGE,IAEG,oBAFH,IAEG;AAAA,IADH;AAAA;AAGA,QAAM,cAAc,eAA2B,OAA6B;AAC5E,QAAM,QAAQ,SAAqB,WAAW;AAE9C,QAAM,eAAe,MAAM,QAAQ,MAAM;AACvC,QAAI,kBAAkB,eAAe,KAAK;AAC1C,UAAM,cAAc,MAAM;AAC1B,UAAM,YAAY,CAAC,kBACjB,eAAe,OAAO,MAAM;AAC1B,wBAAkB,eAAe,KAAK;AACtC,oBAAc;AAAA,IAChB,CAAC;AAEH,WAAO,MAAM;AACX,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,QAAQ,CAAC;AAEpB,SAAO,aAAa;AACtB;","names":[]}
1
+ {"version":3,"sources":["../src/react-hooks.tsx"],"sourcesContent":["import {\n Shape,\n ShapeStream,\n ShapeStreamOptions,\n Row,\n} from '@electric-sql/client'\nimport React from 'react'\nimport { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector.js'\n\nconst streamCache = new Map<string, ShapeStream>()\nconst shapeCache = new Map<ShapeStream, Shape>()\n\nexport async function preloadShape<T extends Row = Row>(\n options: ShapeStreamOptions\n): Promise<Shape<T>> {\n const shapeStream = getShapeStream<T>(options)\n const shape = getShape<T>(shapeStream)\n await shape.value\n return shape\n}\n\nexport function sortedOptionsHash(options: ShapeStreamOptions): string {\n return JSON.stringify(options, Object.keys(options).sort())\n}\n\nexport function getShapeStream<T extends Row = Row>(\n options: ShapeStreamOptions\n): ShapeStream<T> {\n const shapeHash = sortedOptionsHash(options)\n\n // If the stream is already cached, return\n if (streamCache.has(shapeHash)) {\n // Return the ShapeStream\n return streamCache.get(shapeHash)! as ShapeStream<T>\n } else {\n const newShapeStream = new ShapeStream<T>(options)\n\n streamCache.set(shapeHash, newShapeStream)\n\n // Return the created shape\n return newShapeStream\n }\n}\n\nexport function getShape<T extends Row>(shapeStream: ShapeStream<T>): Shape<T> {\n // If the stream is already cached, return\n if (shapeCache.has(shapeStream)) {\n // Return the ShapeStream\n return shapeCache.get(shapeStream)! as Shape<T>\n } else {\n const newShape = new Shape<T>(shapeStream)\n\n shapeCache.set(shapeStream, newShape)\n\n // Return the created shape\n return newShape\n }\n}\n\nexport interface UseShapeResult<T extends Row = Row> {\n /**\n * The array of rows that make up the Shape.\n * @type {T[]}\n */\n data: T[]\n /**\n * The Shape instance used by this useShape\n * @type {Shape<T>}\n */\n shape: Shape<T>\n /** True during initial fetch. False afterwise. */\n isLoading: boolean\n /** Unix time at which we last synced. Undefined when `isLoading` is true. */\n lastSyncedAt?: number\n error: Shape<T>[`error`]\n isError: boolean\n}\n\nfunction shapeSubscribe<T extends Row>(shape: Shape<T>, callback: () => void) {\n const unsubscribe = shape.subscribe(callback)\n return () => {\n unsubscribe()\n }\n}\n\nfunction parseShapeData<T extends Row>(shape: Shape<T>): UseShapeResult<T> {\n return {\n data: [...shape.valueSync.values()],\n isLoading: shape.isLoading(),\n lastSyncedAt: shape.lastSyncedAt(),\n isError: shape.error !== false,\n shape,\n error: shape.error,\n }\n}\n\nfunction identity<T>(arg: T): T {\n return arg\n}\n\ninterface UseShapeOptions<SourceData extends Row, Selection>\n extends ShapeStreamOptions {\n selector?: (value: UseShapeResult<SourceData>) => Selection\n}\n\nexport function useShape<\n SourceData extends Row = Row,\n Selection = UseShapeResult<SourceData>,\n>({\n selector = identity as (arg: UseShapeResult<SourceData>) => Selection,\n ...options\n}: UseShapeOptions<SourceData, Selection>): Selection {\n const shapeStream = getShapeStream<SourceData>(options as ShapeStreamOptions)\n const shape = getShape<SourceData>(shapeStream)\n\n const useShapeData = React.useMemo(() => {\n let latestShapeData = parseShapeData(shape)\n const getSnapshot = () => latestShapeData\n const subscribe = (onStoreChange: () => void) =>\n shapeSubscribe(shape, () => {\n latestShapeData = parseShapeData(shape)\n onStoreChange()\n })\n\n return () => {\n return useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getSnapshot,\n selector\n )\n }\n }, [shape, selector])\n\n return useShapeData()\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAAA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP,OAAO,WAAW;AAClB,SAAS,wCAAwC;AAEjD,IAAM,cAAc,oBAAI,IAAyB;AACjD,IAAM,aAAa,oBAAI,IAAwB;AAE/C,eAAsB,aACpB,SACmB;AACnB,QAAM,cAAc,eAAkB,OAAO;AAC7C,QAAM,QAAQ,SAAY,WAAW;AACrC,QAAM,MAAM;AACZ,SAAO;AACT;AAEO,SAAS,kBAAkB,SAAqC;AACrE,SAAO,KAAK,UAAU,SAAS,OAAO,KAAK,OAAO,EAAE,KAAK,CAAC;AAC5D;AAEO,SAAS,eACd,SACgB;AAChB,QAAM,YAAY,kBAAkB,OAAO;AAG3C,MAAI,YAAY,IAAI,SAAS,GAAG;AAE9B,WAAO,YAAY,IAAI,SAAS;AAAA,EAClC,OAAO;AACL,UAAM,iBAAiB,IAAI,YAAe,OAAO;AAEjD,gBAAY,IAAI,WAAW,cAAc;AAGzC,WAAO;AAAA,EACT;AACF;AAEO,SAAS,SAAwB,aAAuC;AAE7E,MAAI,WAAW,IAAI,WAAW,GAAG;AAE/B,WAAO,WAAW,IAAI,WAAW;AAAA,EACnC,OAAO;AACL,UAAM,WAAW,IAAI,MAAS,WAAW;AAEzC,eAAW,IAAI,aAAa,QAAQ;AAGpC,WAAO;AAAA,EACT;AACF;AAqBA,SAAS,eAA8B,OAAiB,UAAsB;AAC5E,QAAM,cAAc,MAAM,UAAU,QAAQ;AAC5C,SAAO,MAAM;AACX,gBAAY;AAAA,EACd;AACF;AAEA,SAAS,eAA8B,OAAoC;AACzE,SAAO;AAAA,IACL,MAAM,CAAC,GAAG,MAAM,UAAU,OAAO,CAAC;AAAA,IAClC,WAAW,MAAM,UAAU;AAAA,IAC3B,cAAc,MAAM,aAAa;AAAA,IACjC,SAAS,MAAM,UAAU;AAAA,IACzB;AAAA,IACA,OAAO,MAAM;AAAA,EACf;AACF;AAEA,SAAS,SAAY,KAAW;AAC9B,SAAO;AACT;AAOO,SAAS,SAGd,IAGoD;AAHpD,eACA;AAAA,eAAW;AAAA,EA7Gb,IA4GE,IAEG,oBAFH,IAEG;AAAA,IADH;AAAA;AAGA,QAAM,cAAc,eAA2B,OAA6B;AAC5E,QAAM,QAAQ,SAAqB,WAAW;AAE9C,QAAM,eAAe,MAAM,QAAQ,MAAM;AACvC,QAAI,kBAAkB,eAAe,KAAK;AAC1C,UAAM,cAAc,MAAM;AAC1B,UAAM,YAAY,CAAC,kBACjB,eAAe,OAAO,MAAM;AAC1B,wBAAkB,eAAe,KAAK;AACtC,oBAAc;AAAA,IAChB,CAAC;AAEH,WAAO,MAAM;AACX,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,QAAQ,CAAC;AAEpB,SAAO,aAAa;AACtB;","names":[]}
package/dist/index.mjs CHANGED
@@ -83,6 +83,7 @@ function parseShapeData(shape) {
83
83
  return {
84
84
  data: [...shape.valueSync.values()],
85
85
  isLoading: shape.isLoading(),
86
+ lastSyncedAt: shape.lastSyncedAt(),
86
87
  isError: shape.error !== false,
87
88
  shape,
88
89
  error: shape.error
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react-hooks.tsx"],"sourcesContent":["import {\n Shape,\n ShapeStream,\n ShapeStreamOptions,\n Row,\n} from '@electric-sql/client'\nimport React from 'react'\nimport { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector.js'\n\nconst streamCache = new Map<string, ShapeStream>()\nconst shapeCache = new Map<ShapeStream, Shape>()\n\nexport async function preloadShape<T extends Row = Row>(\n options: ShapeStreamOptions\n): Promise<Shape<T>> {\n const shapeStream = getShapeStream<T>(options)\n const shape = getShape<T>(shapeStream)\n await shape.value\n return shape\n}\n\nexport function sortedOptionsHash(options: ShapeStreamOptions): string {\n return JSON.stringify(options, Object.keys(options).sort())\n}\n\nexport function getShapeStream<T extends Row = Row>(\n options: ShapeStreamOptions\n): ShapeStream<T> {\n const shapeHash = sortedOptionsHash(options)\n\n // If the stream is already cached, return\n if (streamCache.has(shapeHash)) {\n // Return the ShapeStream\n return streamCache.get(shapeHash)! as ShapeStream<T>\n } else {\n const newShapeStream = new ShapeStream<T>(options)\n\n streamCache.set(shapeHash, newShapeStream)\n\n // Return the created shape\n return newShapeStream\n }\n}\n\nexport function getShape<T extends Row>(shapeStream: ShapeStream<T>): Shape<T> {\n // If the stream is already cached, return\n if (shapeCache.has(shapeStream)) {\n // Return the ShapeStream\n return shapeCache.get(shapeStream)! as Shape<T>\n } else {\n const newShape = new Shape<T>(shapeStream)\n\n shapeCache.set(shapeStream, newShape)\n\n // Return the created shape\n return newShape\n }\n}\n\nexport interface UseShapeResult<T extends Row = Row> {\n /**\n * The array of rows that make up the Shape.\n * @type {T[]}\n */\n data: T[]\n /**\n * The Shape instance used by this useShape\n * @type {Shape<T>}\n */\n shape: Shape<T>\n /** True during initial fetch. False afterwise. */\n isLoading: boolean\n error: Shape<T>[`error`]\n isError: boolean\n}\n\nfunction shapeSubscribe<T extends Row>(shape: Shape<T>, callback: () => void) {\n const unsubscribe = shape.subscribe(callback)\n return () => {\n unsubscribe()\n }\n}\n\nfunction parseShapeData<T extends Row>(shape: Shape<T>): UseShapeResult<T> {\n return {\n data: [...shape.valueSync.values()],\n isLoading: shape.isLoading(),\n isError: shape.error !== false,\n shape,\n error: shape.error,\n }\n}\n\nfunction identity<T>(arg: T): T {\n return arg\n}\n\ninterface UseShapeOptions<SourceData extends Row, Selection>\n extends ShapeStreamOptions {\n selector?: (value: UseShapeResult<SourceData>) => Selection\n}\n\nexport function useShape<\n SourceData extends Row = Row,\n Selection = UseShapeResult<SourceData>,\n>({\n selector = identity as (arg: UseShapeResult<SourceData>) => Selection,\n ...options\n}: UseShapeOptions<SourceData, Selection>): Selection {\n const shapeStream = getShapeStream<SourceData>(options as ShapeStreamOptions)\n const shape = getShape<SourceData>(shapeStream)\n\n const useShapeData = React.useMemo(() => {\n let latestShapeData = parseShapeData(shape)\n const getSnapshot = () => latestShapeData\n const subscribe = (onStoreChange: () => void) =>\n shapeSubscribe(shape, () => {\n latestShapeData = parseShapeData(shape)\n onStoreChange()\n })\n\n return () => {\n return useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getSnapshot,\n selector\n )\n }\n }, [shape, selector])\n\n return useShapeData()\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP,OAAO,WAAW;AAClB,SAAS,wCAAwC;AAEjD,IAAM,cAAc,oBAAI,IAAyB;AACjD,IAAM,aAAa,oBAAI,IAAwB;AAE/C,SAAsB,aACpB,SACmB;AAAA;AACnB,UAAM,cAAc,eAAkB,OAAO;AAC7C,UAAM,QAAQ,SAAY,WAAW;AACrC,UAAM,MAAM;AACZ,WAAO;AAAA,EACT;AAAA;AAEO,SAAS,kBAAkB,SAAqC;AACrE,SAAO,KAAK,UAAU,SAAS,OAAO,KAAK,OAAO,EAAE,KAAK,CAAC;AAC5D;AAEO,SAAS,eACd,SACgB;AAChB,QAAM,YAAY,kBAAkB,OAAO;AAG3C,MAAI,YAAY,IAAI,SAAS,GAAG;AAE9B,WAAO,YAAY,IAAI,SAAS;AAAA,EAClC,OAAO;AACL,UAAM,iBAAiB,IAAI,YAAe,OAAO;AAEjD,gBAAY,IAAI,WAAW,cAAc;AAGzC,WAAO;AAAA,EACT;AACF;AAEO,SAAS,SAAwB,aAAuC;AAE7E,MAAI,WAAW,IAAI,WAAW,GAAG;AAE/B,WAAO,WAAW,IAAI,WAAW;AAAA,EACnC,OAAO;AACL,UAAM,WAAW,IAAI,MAAS,WAAW;AAEzC,eAAW,IAAI,aAAa,QAAQ;AAGpC,WAAO;AAAA,EACT;AACF;AAmBA,SAAS,eAA8B,OAAiB,UAAsB;AAC5E,QAAM,cAAc,MAAM,UAAU,QAAQ;AAC5C,SAAO,MAAM;AACX,gBAAY;AAAA,EACd;AACF;AAEA,SAAS,eAA8B,OAAoC;AACzE,SAAO;AAAA,IACL,MAAM,CAAC,GAAG,MAAM,UAAU,OAAO,CAAC;AAAA,IAClC,WAAW,MAAM,UAAU;AAAA,IAC3B,SAAS,MAAM,UAAU;AAAA,IACzB;AAAA,IACA,OAAO,MAAM;AAAA,EACf;AACF;AAEA,SAAS,SAAY,KAAW;AAC9B,SAAO;AACT;AAOO,SAAS,SAGd,IAGoD;AAHpD,eACA;AAAA,eAAW;AAAA,EA1Gb,IAyGE,IAEG,oBAFH,IAEG;AAAA,IADH;AAAA;AAGA,QAAM,cAAc,eAA2B,OAA6B;AAC5E,QAAM,QAAQ,SAAqB,WAAW;AAE9C,QAAM,eAAe,MAAM,QAAQ,MAAM;AACvC,QAAI,kBAAkB,eAAe,KAAK;AAC1C,UAAM,cAAc,MAAM;AAC1B,UAAM,YAAY,CAAC,kBACjB,eAAe,OAAO,MAAM;AAC1B,wBAAkB,eAAe,KAAK;AACtC,oBAAc;AAAA,IAChB,CAAC;AAEH,WAAO,MAAM;AACX,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,QAAQ,CAAC;AAEpB,SAAO,aAAa;AACtB;","names":[]}
1
+ {"version":3,"sources":["../src/react-hooks.tsx"],"sourcesContent":["import {\n Shape,\n ShapeStream,\n ShapeStreamOptions,\n Row,\n} from '@electric-sql/client'\nimport React from 'react'\nimport { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector.js'\n\nconst streamCache = new Map<string, ShapeStream>()\nconst shapeCache = new Map<ShapeStream, Shape>()\n\nexport async function preloadShape<T extends Row = Row>(\n options: ShapeStreamOptions\n): Promise<Shape<T>> {\n const shapeStream = getShapeStream<T>(options)\n const shape = getShape<T>(shapeStream)\n await shape.value\n return shape\n}\n\nexport function sortedOptionsHash(options: ShapeStreamOptions): string {\n return JSON.stringify(options, Object.keys(options).sort())\n}\n\nexport function getShapeStream<T extends Row = Row>(\n options: ShapeStreamOptions\n): ShapeStream<T> {\n const shapeHash = sortedOptionsHash(options)\n\n // If the stream is already cached, return\n if (streamCache.has(shapeHash)) {\n // Return the ShapeStream\n return streamCache.get(shapeHash)! as ShapeStream<T>\n } else {\n const newShapeStream = new ShapeStream<T>(options)\n\n streamCache.set(shapeHash, newShapeStream)\n\n // Return the created shape\n return newShapeStream\n }\n}\n\nexport function getShape<T extends Row>(shapeStream: ShapeStream<T>): Shape<T> {\n // If the stream is already cached, return\n if (shapeCache.has(shapeStream)) {\n // Return the ShapeStream\n return shapeCache.get(shapeStream)! as Shape<T>\n } else {\n const newShape = new Shape<T>(shapeStream)\n\n shapeCache.set(shapeStream, newShape)\n\n // Return the created shape\n return newShape\n }\n}\n\nexport interface UseShapeResult<T extends Row = Row> {\n /**\n * The array of rows that make up the Shape.\n * @type {T[]}\n */\n data: T[]\n /**\n * The Shape instance used by this useShape\n * @type {Shape<T>}\n */\n shape: Shape<T>\n /** True during initial fetch. False afterwise. */\n isLoading: boolean\n /** Unix time at which we last synced. Undefined when `isLoading` is true. */\n lastSyncedAt?: number\n error: Shape<T>[`error`]\n isError: boolean\n}\n\nfunction shapeSubscribe<T extends Row>(shape: Shape<T>, callback: () => void) {\n const unsubscribe = shape.subscribe(callback)\n return () => {\n unsubscribe()\n }\n}\n\nfunction parseShapeData<T extends Row>(shape: Shape<T>): UseShapeResult<T> {\n return {\n data: [...shape.valueSync.values()],\n isLoading: shape.isLoading(),\n lastSyncedAt: shape.lastSyncedAt(),\n isError: shape.error !== false,\n shape,\n error: shape.error,\n }\n}\n\nfunction identity<T>(arg: T): T {\n return arg\n}\n\ninterface UseShapeOptions<SourceData extends Row, Selection>\n extends ShapeStreamOptions {\n selector?: (value: UseShapeResult<SourceData>) => Selection\n}\n\nexport function useShape<\n SourceData extends Row = Row,\n Selection = UseShapeResult<SourceData>,\n>({\n selector = identity as (arg: UseShapeResult<SourceData>) => Selection,\n ...options\n}: UseShapeOptions<SourceData, Selection>): Selection {\n const shapeStream = getShapeStream<SourceData>(options as ShapeStreamOptions)\n const shape = getShape<SourceData>(shapeStream)\n\n const useShapeData = React.useMemo(() => {\n let latestShapeData = parseShapeData(shape)\n const getSnapshot = () => latestShapeData\n const subscribe = (onStoreChange: () => void) =>\n shapeSubscribe(shape, () => {\n latestShapeData = parseShapeData(shape)\n onStoreChange()\n })\n\n return () => {\n return useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n getSnapshot,\n selector\n )\n }\n }, [shape, selector])\n\n return useShapeData()\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP,OAAO,WAAW;AAClB,SAAS,wCAAwC;AAEjD,IAAM,cAAc,oBAAI,IAAyB;AACjD,IAAM,aAAa,oBAAI,IAAwB;AAE/C,SAAsB,aACpB,SACmB;AAAA;AACnB,UAAM,cAAc,eAAkB,OAAO;AAC7C,UAAM,QAAQ,SAAY,WAAW;AACrC,UAAM,MAAM;AACZ,WAAO;AAAA,EACT;AAAA;AAEO,SAAS,kBAAkB,SAAqC;AACrE,SAAO,KAAK,UAAU,SAAS,OAAO,KAAK,OAAO,EAAE,KAAK,CAAC;AAC5D;AAEO,SAAS,eACd,SACgB;AAChB,QAAM,YAAY,kBAAkB,OAAO;AAG3C,MAAI,YAAY,IAAI,SAAS,GAAG;AAE9B,WAAO,YAAY,IAAI,SAAS;AAAA,EAClC,OAAO;AACL,UAAM,iBAAiB,IAAI,YAAe,OAAO;AAEjD,gBAAY,IAAI,WAAW,cAAc;AAGzC,WAAO;AAAA,EACT;AACF;AAEO,SAAS,SAAwB,aAAuC;AAE7E,MAAI,WAAW,IAAI,WAAW,GAAG;AAE/B,WAAO,WAAW,IAAI,WAAW;AAAA,EACnC,OAAO;AACL,UAAM,WAAW,IAAI,MAAS,WAAW;AAEzC,eAAW,IAAI,aAAa,QAAQ;AAGpC,WAAO;AAAA,EACT;AACF;AAqBA,SAAS,eAA8B,OAAiB,UAAsB;AAC5E,QAAM,cAAc,MAAM,UAAU,QAAQ;AAC5C,SAAO,MAAM;AACX,gBAAY;AAAA,EACd;AACF;AAEA,SAAS,eAA8B,OAAoC;AACzE,SAAO;AAAA,IACL,MAAM,CAAC,GAAG,MAAM,UAAU,OAAO,CAAC;AAAA,IAClC,WAAW,MAAM,UAAU;AAAA,IAC3B,cAAc,MAAM,aAAa;AAAA,IACjC,SAAS,MAAM,UAAU;AAAA,IACzB;AAAA,IACA,OAAO,MAAM;AAAA,EACf;AACF;AAEA,SAAS,SAAY,KAAW;AAC9B,SAAO;AACT;AAOO,SAAS,SAGd,IAGoD;AAHpD,eACA;AAAA,eAAW;AAAA,EA7Gb,IA4GE,IAEG,oBAFH,IAEG;AAAA,IADH;AAAA;AAGA,QAAM,cAAc,eAA2B,OAA6B;AAC5E,QAAM,QAAQ,SAAqB,WAAW;AAE9C,QAAM,eAAe,MAAM,QAAQ,MAAM;AACvC,QAAI,kBAAkB,eAAe,KAAK;AAC1C,UAAM,cAAc,MAAM;AAC1B,UAAM,YAAY,CAAC,kBACjB,eAAe,OAAO,MAAM;AAC1B,wBAAkB,eAAe,KAAK;AACtC,oBAAc;AAAA,IAChB,CAAC;AAEH,WAAO,MAAM;AACX,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,QAAQ,CAAC;AAEpB,SAAO,aAAa;AACtB;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electric-sql/react",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "React hooks for ElectricSQL",
5
5
  "type": "module",
6
6
  "main": "dist/cjs/index.cjs",
@@ -31,7 +31,7 @@
31
31
  "homepage": "https://electric-sql.com",
32
32
  "dependencies": {
33
33
  "use-sync-external-store": "^1.2.2",
34
- "@electric-sql/client": "0.5.0"
34
+ "@electric-sql/client": "0.5.1"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@testing-library/react": "^16.0.0",
@@ -70,6 +70,8 @@ export interface UseShapeResult<T extends Row = Row> {
70
70
  shape: Shape<T>
71
71
  /** True during initial fetch. False afterwise. */
72
72
  isLoading: boolean
73
+ /** Unix time at which we last synced. Undefined when `isLoading` is true. */
74
+ lastSyncedAt?: number
73
75
  error: Shape<T>[`error`]
74
76
  isError: boolean
75
77
  }
@@ -85,6 +87,7 @@ function parseShapeData<T extends Row>(shape: Shape<T>): UseShapeResult<T> {
85
87
  return {
86
88
  data: [...shape.valueSync.values()],
87
89
  isLoading: shape.isLoading(),
90
+ lastSyncedAt: shape.lastSyncedAt(),
88
91
  isError: shape.error !== false,
89
92
  shape,
90
93
  error: shape.error,