@electric-sql/react 0.0.9 → 0.0.10
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/cjs/index.cjs +184 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/index.browser.mjs +2 -0
- package/dist/index.browser.mjs.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.legacy-esm.js +125 -0
- package/dist/index.legacy-esm.js.map +1 -0
- package/dist/index.mjs +147 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +3 -3
- package/src/react-hooks.tsx +1 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
9
|
+
var __objRest = (source, exclude) => {
|
|
10
|
+
var target = {};
|
|
11
|
+
for (var prop in source)
|
|
12
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
13
|
+
target[prop] = source[prop];
|
|
14
|
+
if (source != null && __getOwnPropSymbols)
|
|
15
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
16
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
17
|
+
target[prop] = source[prop];
|
|
18
|
+
}
|
|
19
|
+
return target;
|
|
20
|
+
};
|
|
21
|
+
var __export = (target, all) => {
|
|
22
|
+
for (var name in all)
|
|
23
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
24
|
+
};
|
|
25
|
+
var __copyProps = (to, from, except, desc) => {
|
|
26
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
27
|
+
for (let key of __getOwnPropNames(from))
|
|
28
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
29
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
30
|
+
}
|
|
31
|
+
return to;
|
|
32
|
+
};
|
|
33
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
34
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
35
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
36
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
37
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
38
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
39
|
+
mod
|
|
40
|
+
));
|
|
41
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
42
|
+
var __async = (__this, __arguments, generator) => {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
var fulfilled = (value) => {
|
|
45
|
+
try {
|
|
46
|
+
step(generator.next(value));
|
|
47
|
+
} catch (e) {
|
|
48
|
+
reject(e);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
var rejected = (value) => {
|
|
52
|
+
try {
|
|
53
|
+
step(generator.throw(value));
|
|
54
|
+
} catch (e) {
|
|
55
|
+
reject(e);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
59
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// src/index.ts
|
|
64
|
+
var src_exports = {};
|
|
65
|
+
__export(src_exports, {
|
|
66
|
+
ShapesProvider: () => ShapesProvider,
|
|
67
|
+
getShape: () => getShape,
|
|
68
|
+
getShapeStream: () => getShapeStream,
|
|
69
|
+
preloadShape: () => preloadShape,
|
|
70
|
+
sortedOptionsHash: () => sortedOptionsHash,
|
|
71
|
+
useShape: () => useShape,
|
|
72
|
+
useShapeContext: () => useShapeContext
|
|
73
|
+
});
|
|
74
|
+
module.exports = __toCommonJS(src_exports);
|
|
75
|
+
|
|
76
|
+
// src/react-hooks.tsx
|
|
77
|
+
var import_next = require("@electric-sql/next");
|
|
78
|
+
var import_react = __toESM(require("react"), 1);
|
|
79
|
+
var import_with_selector = require("use-sync-external-store/with-selector.js");
|
|
80
|
+
var ShapesContext = (0, import_react.createContext)(null);
|
|
81
|
+
var streamCache = /* @__PURE__ */ new Map();
|
|
82
|
+
var shapeCache = /* @__PURE__ */ new Map();
|
|
83
|
+
function preloadShape(options) {
|
|
84
|
+
return __async(this, null, function* () {
|
|
85
|
+
const shapeStream = getShapeStream(options);
|
|
86
|
+
const shape = getShape(shapeStream);
|
|
87
|
+
yield shape.value;
|
|
88
|
+
return shape;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function sortedOptionsHash(options) {
|
|
92
|
+
const shapeDef = JSON.stringify(
|
|
93
|
+
options.shape,
|
|
94
|
+
Object.keys(options.shape).sort()
|
|
95
|
+
);
|
|
96
|
+
const _a = options, { shape } = _a, optionsWithoutShapeDef = __objRest(_a, ["shape"]);
|
|
97
|
+
const allOptions = JSON.stringify(
|
|
98
|
+
optionsWithoutShapeDef,
|
|
99
|
+
Object.keys(options).sort()
|
|
100
|
+
);
|
|
101
|
+
const shapeHash = shapeDef + allOptions;
|
|
102
|
+
return shapeHash;
|
|
103
|
+
}
|
|
104
|
+
function getShapeStream(options) {
|
|
105
|
+
const shapeHash = sortedOptionsHash(options);
|
|
106
|
+
if (streamCache.has(shapeHash)) {
|
|
107
|
+
return streamCache.get(shapeHash);
|
|
108
|
+
} else {
|
|
109
|
+
const newShapeStream = new import_next.ShapeStream(options);
|
|
110
|
+
streamCache.set(shapeHash, newShapeStream);
|
|
111
|
+
return newShapeStream;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function getShape(shapeStream) {
|
|
115
|
+
if (shapeCache.has(shapeStream)) {
|
|
116
|
+
return shapeCache.get(shapeStream);
|
|
117
|
+
} else {
|
|
118
|
+
const newShape = new import_next.Shape(shapeStream);
|
|
119
|
+
shapeCache.set(shapeStream, newShape);
|
|
120
|
+
return newShape;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function ShapesProvider({ children }) {
|
|
124
|
+
return /* @__PURE__ */ import_react.default.createElement(ShapesContext.Provider, { value: { getShape, getShapeStream } }, children);
|
|
125
|
+
}
|
|
126
|
+
function useShapeContext() {
|
|
127
|
+
const context = (0, import_react.useContext)(ShapesContext);
|
|
128
|
+
if (!context) {
|
|
129
|
+
throw new Error(`useShapeContext must be used within a ShapeProvider`);
|
|
130
|
+
}
|
|
131
|
+
return context;
|
|
132
|
+
}
|
|
133
|
+
function shapeSubscribe(shape, callback) {
|
|
134
|
+
const unsubscribe = shape.subscribe(callback);
|
|
135
|
+
return () => {
|
|
136
|
+
unsubscribe();
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function parseShapeData(shape) {
|
|
140
|
+
return {
|
|
141
|
+
data: [...shape.valueSync.values()],
|
|
142
|
+
isUpToDate: shape.isUpToDate,
|
|
143
|
+
isError: shape.error !== false,
|
|
144
|
+
shape,
|
|
145
|
+
error: shape.error
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
var identity = (arg) => arg;
|
|
149
|
+
function useShape(_a) {
|
|
150
|
+
var _b = _a, {
|
|
151
|
+
selector = identity
|
|
152
|
+
} = _b, options = __objRest(_b, [
|
|
153
|
+
"selector"
|
|
154
|
+
]);
|
|
155
|
+
const { getShape: getShape2, getShapeStream: getShapeStream2 } = useShapeContext();
|
|
156
|
+
const shapeStream = getShapeStream2(options);
|
|
157
|
+
const shape = getShape2(shapeStream);
|
|
158
|
+
const latestShapeData = (0, import_react.useRef)(parseShapeData(shape));
|
|
159
|
+
const getSnapshot = import_react.default.useCallback(() => latestShapeData.current, []);
|
|
160
|
+
const shapeData = (0, import_with_selector.useSyncExternalStoreWithSelector)(
|
|
161
|
+
(0, import_react.useCallback)(
|
|
162
|
+
(onStoreChange) => shapeSubscribe(shape, () => {
|
|
163
|
+
latestShapeData.current = parseShapeData(shape);
|
|
164
|
+
onStoreChange();
|
|
165
|
+
}),
|
|
166
|
+
[shape]
|
|
167
|
+
),
|
|
168
|
+
getSnapshot,
|
|
169
|
+
getSnapshot,
|
|
170
|
+
selector
|
|
171
|
+
);
|
|
172
|
+
return shapeData;
|
|
173
|
+
}
|
|
174
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
175
|
+
0 && (module.exports = {
|
|
176
|
+
ShapesProvider,
|
|
177
|
+
getShape,
|
|
178
|
+
getShapeStream,
|
|
179
|
+
preloadShape,
|
|
180
|
+
sortedOptionsHash,
|
|
181
|
+
useShape,
|
|
182
|
+
useShapeContext
|
|
183
|
+
});
|
|
184
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts","../../src/react-hooks.tsx"],"sourcesContent":["export * from './react-hooks'\n","import {\n JsonSerializable,\n Shape,\n ShapeStream,\n ShapeStreamOptions,\n} from '@electric-sql/next'\nimport React, { createContext, useCallback, useContext, useRef } from 'react'\nimport { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector.js'\n\ninterface ShapeContextType {\n getShape: (shapeStream: ShapeStream) => Shape\n getShapeStream: (options: ShapeStreamOptions) => ShapeStream\n}\n\n// Create a Context\nconst ShapesContext = createContext<ShapeContextType | null>(null)\n\nconst streamCache = new Map<string, ShapeStream>()\nconst shapeCache = new Map<ShapeStream, Shape>()\n\nexport async function preloadShape(\n options: ShapeStreamOptions\n): Promise<Shape> {\n const shapeStream = getShapeStream(options)\n const shape = getShape(shapeStream)\n await shape.value\n return shape\n}\n\nexport function sortedOptionsHash(options: ShapeStreamOptions): string {\n const shapeDef = JSON.stringify(\n options.shape,\n Object.keys(options.shape).sort()\n )\n // eslint-disable-next-line\n const { shape, ...optionsWithoutShapeDef } = options\n const allOptions = JSON.stringify(\n optionsWithoutShapeDef,\n Object.keys(options).sort()\n )\n const shapeHash = shapeDef + allOptions\n\n return shapeHash\n}\n\nexport function getShapeStream(options: ShapeStreamOptions): ShapeStream {\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)!\n } else {\n const newShapeStream = new ShapeStream(options)\n\n streamCache.set(shapeHash, newShapeStream)\n\n // Return the created shape\n return newShapeStream\n }\n}\n\nexport function getShape(shapeStream: ShapeStream): Shape {\n // If the stream is already cached, return\n if (shapeCache.has(shapeStream)) {\n // Return the ShapeStream\n return shapeCache.get(shapeStream)!\n } else {\n const newShape = new Shape(shapeStream)\n\n shapeCache.set(shapeStream, newShape)\n\n // Return the created shape\n return newShape\n }\n}\n\ninterface ShapeProviderProps {\n children: React.ReactNode\n}\n\n// Shapes Provider Component\nexport function ShapesProvider({ children }: ShapeProviderProps): JSX.Element {\n // Provide the context value\n return (\n <ShapesContext.Provider value={{ getShape, getShapeStream }}>\n {children}\n </ShapesContext.Provider>\n )\n}\n\nexport function useShapeContext() {\n const context = useContext(ShapesContext)\n if (!context) {\n throw new Error(`useShapeContext must be used within a ShapeProvider`)\n }\n return context\n}\n\ninterface UseShapeResult {\n /**\n * The array of rows that make up the Shape.\n * @type {{ [key: string]: JsonSerializable }[]}\n */\n data: { [key: string]: JsonSerializable }[]\n /**\n * The Shape instance used by this useShape\n * @type(Shape)\n */\n shape: Shape\n error: Shape[`error`]\n isError: boolean\n /**\n * Has the ShapeStream caught up with the replication log from Postgres.\n */\n isUpToDate: boolean\n}\n\nfunction shapeSubscribe(shape: Shape, callback: () => void) {\n const unsubscribe = shape.subscribe(callback)\n return () => {\n unsubscribe()\n }\n}\n\nfunction parseShapeData(shape: Shape): UseShapeResult {\n return {\n data: [...shape.valueSync.values()],\n isUpToDate: shape.isUpToDate,\n isError: shape.error !== false,\n shape,\n error: shape.error,\n }\n}\n\nconst identity = (arg: unknown) => arg\n\ninterface UseShapeOptions<Selection> extends ShapeStreamOptions {\n selector?: (value: UseShapeResult) => Selection\n}\n\nexport function useShape<Selection = UseShapeResult>({\n selector = identity as never,\n ...options\n}: UseShapeOptions<Selection>): Selection {\n const { getShape, getShapeStream } = useShapeContext()\n const shapeStream = getShapeStream(options as ShapeStreamOptions)\n const shape = getShape(shapeStream)\n\n const latestShapeData = useRef(parseShapeData(shape))\n const getSnapshot = React.useCallback(() => latestShapeData.current, [])\n const shapeData = useSyncExternalStoreWithSelector(\n useCallback(\n (onStoreChange) =>\n shapeSubscribe(shape, () => {\n latestShapeData.current = parseShapeData(shape)\n onStoreChange()\n }),\n [shape]\n ),\n getSnapshot,\n getSnapshot,\n selector\n )\n\n return shapeData\n}\n"],"mappings":"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","names":["React","getShape","getShapeStream"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var l=Object.getOwnPropertySymbols;var v=Object.prototype.hasOwnProperty,C=Object.prototype.propertyIsEnumerable;var i=(e,t)=>{var a={};for(var r in e)v.call(e,r)&&t.indexOf(r)<0&&(a[r]=e[r]);if(e!=null&&l)for(var r of l(e))t.indexOf(r)<0&&C.call(e,r)&&(a[r]=e[r]);return a};var m=(e,t,a)=>new Promise((r,o)=>{var h=n=>{try{s(a.next(n))}catch(S){o(S)}},p=n=>{try{s(a.throw(n))}catch(S){o(S)}},s=n=>n.done?r(n.value):Promise.resolve(n.value).then(h,p);s((a=a.apply(e,t)).next())});import{Shape as w,ShapeStream as y}from"@electric-sql/next";import x,{createContext as D,useCallback as P,useContext as U,useRef as R}from"react";import{useSyncExternalStoreWithSelector as k}from"use-sync-external-store/with-selector.js";var d=D(null),c=new Map,u=new Map;function A(e){return m(this,null,function*(){let t=b(e),a=O(t);return yield a.value,a})}function E(e){let t=JSON.stringify(e.shape,Object.keys(e.shape).sort()),p=e,{shape:a}=p,r=i(p,["shape"]),o=JSON.stringify(r,Object.keys(e).sort());return t+o}function b(e){let t=E(e);if(c.has(t))return c.get(t);{let a=new y(e);return c.set(t,a),a}}function O(e){if(u.has(e))return u.get(e);{let t=new w(e);return u.set(e,t),t}}function B({children:e}){return x.createElement(d.Provider,{value:{getShape:O,getShapeStream:b}},e)}function J(){let e=U(d);if(!e)throw new Error("useShapeContext must be used within a ShapeProvider");return e}function T(e,t){let a=e.subscribe(t);return()=>{a()}}function f(e){return{data:[...e.valueSync.values()],isUpToDate:e.isUpToDate,isError:e.error!==!1,shape:e,error:e.error}}var H=e=>e;function F(a){var r=a,{selector:e=H}=r,t=i(r,["selector"]);let{getShape:o,getShapeStream:h}=J(),p=h(t),s=o(p),n=R(f(s)),S=x.useCallback(()=>n.current,[]);return k(P(g=>T(s,()=>{n.current=f(s),g()}),[s]),S,S,e)}export{B as ShapesProvider,O as getShape,b as getShapeStream,A as preloadShape,E as sortedOptionsHash,F as useShape,J as useShapeContext};
|
|
2
|
+
//# sourceMappingURL=index.browser.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react-hooks.tsx"],"sourcesContent":["import {\n JsonSerializable,\n Shape,\n ShapeStream,\n ShapeStreamOptions,\n} from '@electric-sql/next'\nimport React, { createContext, useCallback, useContext, useRef } from 'react'\nimport { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector.js'\n\ninterface ShapeContextType {\n getShape: (shapeStream: ShapeStream) => Shape\n getShapeStream: (options: ShapeStreamOptions) => ShapeStream\n}\n\n// Create a Context\nconst ShapesContext = createContext<ShapeContextType | null>(null)\n\nconst streamCache = new Map<string, ShapeStream>()\nconst shapeCache = new Map<ShapeStream, Shape>()\n\nexport async function preloadShape(\n options: ShapeStreamOptions\n): Promise<Shape> {\n const shapeStream = getShapeStream(options)\n const shape = getShape(shapeStream)\n await shape.value\n return shape\n}\n\nexport function sortedOptionsHash(options: ShapeStreamOptions): string {\n const shapeDef = JSON.stringify(\n options.shape,\n Object.keys(options.shape).sort()\n )\n // eslint-disable-next-line\n const { shape, ...optionsWithoutShapeDef } = options\n const allOptions = JSON.stringify(\n optionsWithoutShapeDef,\n Object.keys(options).sort()\n )\n const shapeHash = shapeDef + allOptions\n\n return shapeHash\n}\n\nexport function getShapeStream(options: ShapeStreamOptions): ShapeStream {\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)!\n } else {\n const newShapeStream = new ShapeStream(options)\n\n streamCache.set(shapeHash, newShapeStream)\n\n // Return the created shape\n return newShapeStream\n }\n}\n\nexport function getShape(shapeStream: ShapeStream): Shape {\n // If the stream is already cached, return\n if (shapeCache.has(shapeStream)) {\n // Return the ShapeStream\n return shapeCache.get(shapeStream)!\n } else {\n const newShape = new Shape(shapeStream)\n\n shapeCache.set(shapeStream, newShape)\n\n // Return the created shape\n return newShape\n }\n}\n\ninterface ShapeProviderProps {\n children: React.ReactNode\n}\n\n// Shapes Provider Component\nexport function ShapesProvider({ children }: ShapeProviderProps): JSX.Element {\n // Provide the context value\n return (\n <ShapesContext.Provider value={{ getShape, getShapeStream }}>\n {children}\n </ShapesContext.Provider>\n )\n}\n\nexport function useShapeContext() {\n const context = useContext(ShapesContext)\n if (!context) {\n throw new Error(`useShapeContext must be used within a ShapeProvider`)\n }\n return context\n}\n\ninterface UseShapeResult {\n /**\n * The array of rows that make up the Shape.\n * @type {{ [key: string]: JsonSerializable }[]}\n */\n data: { [key: string]: JsonSerializable }[]\n /**\n * The Shape instance used by this useShape\n * @type(Shape)\n */\n shape: Shape\n error: Shape[`error`]\n isError: boolean\n /**\n * Has the ShapeStream caught up with the replication log from Postgres.\n */\n isUpToDate: boolean\n}\n\nfunction shapeSubscribe(shape: Shape, callback: () => void) {\n const unsubscribe = shape.subscribe(callback)\n return () => {\n unsubscribe()\n }\n}\n\nfunction parseShapeData(shape: Shape): UseShapeResult {\n return {\n data: [...shape.valueSync.values()],\n isUpToDate: shape.isUpToDate,\n isError: shape.error !== false,\n shape,\n error: shape.error,\n }\n}\n\nconst identity = (arg: unknown) => arg\n\ninterface UseShapeOptions<Selection> extends ShapeStreamOptions {\n selector?: (value: UseShapeResult) => Selection\n}\n\nexport function useShape<Selection = UseShapeResult>({\n selector = identity as never,\n ...options\n}: UseShapeOptions<Selection>): Selection {\n const { getShape, getShapeStream } = useShapeContext()\n const shapeStream = getShapeStream(options as ShapeStreamOptions)\n const shape = getShape(shapeStream)\n\n const latestShapeData = useRef(parseShapeData(shape))\n const getSnapshot = React.useCallback(() => latestShapeData.current, [])\n const shapeData = useSyncExternalStoreWithSelector(\n useCallback(\n (onStoreChange) =>\n shapeSubscribe(shape, () => {\n latestShapeData.current = parseShapeData(shape)\n onStoreChange()\n }),\n [shape]\n ),\n getSnapshot,\n getSnapshot,\n selector\n )\n\n return shapeData\n}\n"],"mappings":"geAAA,OAEE,SAAAA,EACA,eAAAC,MAEK,qBACP,OAAOC,GAAS,iBAAAC,EAAe,eAAAC,EAAa,cAAAC,EAAY,UAAAC,MAAc,QACtE,OAAS,oCAAAC,MAAwC,2CAQjD,IAAMC,EAAgBC,EAAuC,IAAI,EAE3DC,EAAc,IAAI,IAClBC,EAAa,IAAI,IAEvB,SAAsBC,EACpBC,EACgB,QAAAC,EAAA,sBAChB,IAAMC,EAAcC,EAAeH,CAAO,EACpCI,EAAQC,EAASH,CAAW,EAClC,aAAME,EAAM,MACLA,CACT,GAEO,SAASE,EAAkBN,EAAqC,CACrE,IAAMO,EAAW,KAAK,UACpBP,EAAQ,MACR,OAAO,KAAKA,EAAQ,KAAK,EAAE,KAAK,CAClC,EAE6CQ,EAAAR,EAArC,OAAAI,CAnCV,EAmC+CI,EAA3BC,EAAAC,EAA2BF,EAA3B,CAAV,UACFG,EAAa,KAAK,UACtBF,EACA,OAAO,KAAKT,CAAO,EAAE,KAAK,CAC5B,EAGA,OAFkBO,EAAWI,CAG/B,CAEO,SAASR,EAAeH,EAA0C,CACvE,IAAMY,EAAYN,EAAkBN,CAAO,EAG3C,GAAIH,EAAY,IAAIe,CAAS,EAE3B,OAAOf,EAAY,IAAIe,CAAS,EAC3B,CACL,IAAMC,EAAiB,IAAIC,EAAYd,CAAO,EAE9C,OAAAH,EAAY,IAAIe,EAAWC,CAAc,EAGlCA,CACT,CACF,CAEO,SAASR,EAASH,EAAiC,CAExD,GAAIJ,EAAW,IAAII,CAAW,EAE5B,OAAOJ,EAAW,IAAII,CAAW,EAC5B,CACL,IAAMa,EAAW,IAAIC,EAAMd,CAAW,EAEtC,OAAAJ,EAAW,IAAII,EAAaa,CAAQ,EAG7BA,CACT,CACF,CAOO,SAASE,EAAe,CAAE,SAAAC,CAAS,EAAoC,CAE5E,OACEC,EAAA,cAACxB,EAAc,SAAd,CAAuB,MAAO,CAAE,SAAAU,EAAU,eAAAF,CAAe,GACvDe,CACH,CAEJ,CAEO,SAASE,GAAkB,CAChC,IAAMC,EAAUC,EAAW3B,CAAa,EACxC,GAAI,CAAC0B,EACH,MAAM,IAAI,MAAM,qDAAqD,EAEvE,OAAOA,CACT,CAqBA,SAASE,EAAenB,EAAcoB,EAAsB,CAC1D,IAAMC,EAAcrB,EAAM,UAAUoB,CAAQ,EAC5C,MAAO,IAAM,CACXC,EAAY,CACd,CACF,CAEA,SAASC,EAAetB,EAA8B,CACpD,MAAO,CACL,KAAM,CAAC,GAAGA,EAAM,UAAU,OAAO,CAAC,EAClC,WAAYA,EAAM,WAClB,QAASA,EAAM,QAAU,GACzB,MAAAA,EACA,MAAOA,EAAM,KACf,CACF,CAEA,IAAMuB,EAAYC,GAAiBA,EAM5B,SAASC,EAAqCrB,EAGX,CAHW,IAAAsB,EAAAtB,EACnD,UAAAuB,EAAWJ,CA9Ib,EA6IqDG,EAEhD9B,EAAAU,EAFgDoB,EAEhD,CADH,aAGA,GAAM,CAAE,SAAAzB,EAAU,eAAAF,CAAe,EAAIiB,EAAgB,EAC/ClB,EAAcC,EAAeH,CAA6B,EAC1DI,EAAQC,EAASH,CAAW,EAE5B8B,EAAkBC,EAAOP,EAAetB,CAAK,CAAC,EAC9C8B,EAAcf,EAAM,YAAY,IAAMa,EAAgB,QAAS,CAAC,CAAC,EAevE,OAdkBG,EAChBC,EACGC,GACCd,EAAenB,EAAO,IAAM,CAC1B4B,EAAgB,QAAUN,EAAetB,CAAK,EAC9CiC,EAAc,CAChB,CAAC,EACH,CAACjC,CAAK,CACR,EACA8B,EACAA,EACAH,CACF,CAGF","names":["Shape","ShapeStream","React","createContext","useCallback","useContext","useRef","useSyncExternalStoreWithSelector","ShapesContext","createContext","streamCache","shapeCache","preloadShape","options","__async","shapeStream","getShapeStream","shape","getShape","sortedOptionsHash","shapeDef","_a","optionsWithoutShapeDef","__objRest","allOptions","shapeHash","newShapeStream","ShapeStream","newShape","Shape","ShapesProvider","children","React","useShapeContext","context","useContext","shapeSubscribe","callback","unsubscribe","parseShapeData","identity","arg","useShape","_b","selector","latestShapeData","useRef","getSnapshot","useSyncExternalStoreWithSelector","useCallback","onStoreChange"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ShapeStreamOptions, Shape, ShapeStream, JsonSerializable } from '@electric-sql/next';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
interface ShapeContextType {
|
|
5
|
+
getShape: (shapeStream: ShapeStream) => Shape;
|
|
6
|
+
getShapeStream: (options: ShapeStreamOptions) => ShapeStream;
|
|
7
|
+
}
|
|
8
|
+
declare function preloadShape(options: ShapeStreamOptions): Promise<Shape>;
|
|
9
|
+
declare function sortedOptionsHash(options: ShapeStreamOptions): string;
|
|
10
|
+
declare function getShapeStream(options: ShapeStreamOptions): ShapeStream;
|
|
11
|
+
declare function getShape(shapeStream: ShapeStream): Shape;
|
|
12
|
+
interface ShapeProviderProps {
|
|
13
|
+
children: React.ReactNode;
|
|
14
|
+
}
|
|
15
|
+
declare function ShapesProvider({ children }: ShapeProviderProps): JSX.Element;
|
|
16
|
+
declare function useShapeContext(): ShapeContextType;
|
|
17
|
+
interface UseShapeResult {
|
|
18
|
+
/**
|
|
19
|
+
* The array of rows that make up the Shape.
|
|
20
|
+
* @type {{ [key: string]: JsonSerializable }[]}
|
|
21
|
+
*/
|
|
22
|
+
data: {
|
|
23
|
+
[key: string]: JsonSerializable;
|
|
24
|
+
}[];
|
|
25
|
+
/**
|
|
26
|
+
* The Shape instance used by this useShape
|
|
27
|
+
* @type(Shape)
|
|
28
|
+
*/
|
|
29
|
+
shape: Shape;
|
|
30
|
+
error: Shape[`error`];
|
|
31
|
+
isError: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Has the ShapeStream caught up with the replication log from Postgres.
|
|
34
|
+
*/
|
|
35
|
+
isUpToDate: boolean;
|
|
36
|
+
}
|
|
37
|
+
interface UseShapeOptions<Selection> extends ShapeStreamOptions {
|
|
38
|
+
selector?: (value: UseShapeResult) => Selection;
|
|
39
|
+
}
|
|
40
|
+
declare function useShape<Selection = UseShapeResult>({ selector, ...options }: UseShapeOptions<Selection>): Selection;
|
|
41
|
+
|
|
42
|
+
export { ShapesProvider, getShape, getShapeStream, preloadShape, sortedOptionsHash, useShape, useShapeContext };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
2
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
3
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
4
|
+
var __objRest = (source, exclude) => {
|
|
5
|
+
var target = {};
|
|
6
|
+
for (var prop in source)
|
|
7
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
8
|
+
target[prop] = source[prop];
|
|
9
|
+
if (source != null && __getOwnPropSymbols)
|
|
10
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
11
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
12
|
+
target[prop] = source[prop];
|
|
13
|
+
}
|
|
14
|
+
return target;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/react-hooks.tsx
|
|
18
|
+
import {
|
|
19
|
+
Shape,
|
|
20
|
+
ShapeStream
|
|
21
|
+
} from "@electric-sql/next";
|
|
22
|
+
import React, { createContext, useCallback, useContext, useRef } from "react";
|
|
23
|
+
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector.js";
|
|
24
|
+
var ShapesContext = createContext(null);
|
|
25
|
+
var streamCache = /* @__PURE__ */ new Map();
|
|
26
|
+
var shapeCache = /* @__PURE__ */ new Map();
|
|
27
|
+
async function preloadShape(options) {
|
|
28
|
+
const shapeStream = getShapeStream(options);
|
|
29
|
+
const shape = getShape(shapeStream);
|
|
30
|
+
await shape.value;
|
|
31
|
+
return shape;
|
|
32
|
+
}
|
|
33
|
+
function sortedOptionsHash(options) {
|
|
34
|
+
const shapeDef = JSON.stringify(
|
|
35
|
+
options.shape,
|
|
36
|
+
Object.keys(options.shape).sort()
|
|
37
|
+
);
|
|
38
|
+
const _a = options, { shape } = _a, optionsWithoutShapeDef = __objRest(_a, ["shape"]);
|
|
39
|
+
const allOptions = JSON.stringify(
|
|
40
|
+
optionsWithoutShapeDef,
|
|
41
|
+
Object.keys(options).sort()
|
|
42
|
+
);
|
|
43
|
+
const shapeHash = shapeDef + allOptions;
|
|
44
|
+
return shapeHash;
|
|
45
|
+
}
|
|
46
|
+
function getShapeStream(options) {
|
|
47
|
+
const shapeHash = sortedOptionsHash(options);
|
|
48
|
+
if (streamCache.has(shapeHash)) {
|
|
49
|
+
return streamCache.get(shapeHash);
|
|
50
|
+
} else {
|
|
51
|
+
const newShapeStream = new ShapeStream(options);
|
|
52
|
+
streamCache.set(shapeHash, newShapeStream);
|
|
53
|
+
return newShapeStream;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function getShape(shapeStream) {
|
|
57
|
+
if (shapeCache.has(shapeStream)) {
|
|
58
|
+
return shapeCache.get(shapeStream);
|
|
59
|
+
} else {
|
|
60
|
+
const newShape = new Shape(shapeStream);
|
|
61
|
+
shapeCache.set(shapeStream, newShape);
|
|
62
|
+
return newShape;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function ShapesProvider({ children }) {
|
|
66
|
+
return /* @__PURE__ */ React.createElement(ShapesContext.Provider, { value: { getShape, getShapeStream } }, children);
|
|
67
|
+
}
|
|
68
|
+
function useShapeContext() {
|
|
69
|
+
const context = useContext(ShapesContext);
|
|
70
|
+
if (!context) {
|
|
71
|
+
throw new Error(`useShapeContext must be used within a ShapeProvider`);
|
|
72
|
+
}
|
|
73
|
+
return context;
|
|
74
|
+
}
|
|
75
|
+
function shapeSubscribe(shape, callback) {
|
|
76
|
+
const unsubscribe = shape.subscribe(callback);
|
|
77
|
+
return () => {
|
|
78
|
+
unsubscribe();
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function parseShapeData(shape) {
|
|
82
|
+
return {
|
|
83
|
+
data: [...shape.valueSync.values()],
|
|
84
|
+
isUpToDate: shape.isUpToDate,
|
|
85
|
+
isError: shape.error !== false,
|
|
86
|
+
shape,
|
|
87
|
+
error: shape.error
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
var identity = (arg) => arg;
|
|
91
|
+
function useShape(_a) {
|
|
92
|
+
var _b = _a, {
|
|
93
|
+
selector = identity
|
|
94
|
+
} = _b, options = __objRest(_b, [
|
|
95
|
+
"selector"
|
|
96
|
+
]);
|
|
97
|
+
const { getShape: getShape2, getShapeStream: getShapeStream2 } = useShapeContext();
|
|
98
|
+
const shapeStream = getShapeStream2(options);
|
|
99
|
+
const shape = getShape2(shapeStream);
|
|
100
|
+
const latestShapeData = useRef(parseShapeData(shape));
|
|
101
|
+
const getSnapshot = React.useCallback(() => latestShapeData.current, []);
|
|
102
|
+
const shapeData = useSyncExternalStoreWithSelector(
|
|
103
|
+
useCallback(
|
|
104
|
+
(onStoreChange) => shapeSubscribe(shape, () => {
|
|
105
|
+
latestShapeData.current = parseShapeData(shape);
|
|
106
|
+
onStoreChange();
|
|
107
|
+
}),
|
|
108
|
+
[shape]
|
|
109
|
+
),
|
|
110
|
+
getSnapshot,
|
|
111
|
+
getSnapshot,
|
|
112
|
+
selector
|
|
113
|
+
);
|
|
114
|
+
return shapeData;
|
|
115
|
+
}
|
|
116
|
+
export {
|
|
117
|
+
ShapesProvider,
|
|
118
|
+
getShape,
|
|
119
|
+
getShapeStream,
|
|
120
|
+
preloadShape,
|
|
121
|
+
sortedOptionsHash,
|
|
122
|
+
useShape,
|
|
123
|
+
useShapeContext
|
|
124
|
+
};
|
|
125
|
+
//# sourceMappingURL=index.legacy-esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react-hooks.tsx"],"sourcesContent":["import {\n JsonSerializable,\n Shape,\n ShapeStream,\n ShapeStreamOptions,\n} from '@electric-sql/next'\nimport React, { createContext, useCallback, useContext, useRef } from 'react'\nimport { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector.js'\n\ninterface ShapeContextType {\n getShape: (shapeStream: ShapeStream) => Shape\n getShapeStream: (options: ShapeStreamOptions) => ShapeStream\n}\n\n// Create a Context\nconst ShapesContext = createContext<ShapeContextType | null>(null)\n\nconst streamCache = new Map<string, ShapeStream>()\nconst shapeCache = new Map<ShapeStream, Shape>()\n\nexport async function preloadShape(\n options: ShapeStreamOptions\n): Promise<Shape> {\n const shapeStream = getShapeStream(options)\n const shape = getShape(shapeStream)\n await shape.value\n return shape\n}\n\nexport function sortedOptionsHash(options: ShapeStreamOptions): string {\n const shapeDef = JSON.stringify(\n options.shape,\n Object.keys(options.shape).sort()\n )\n // eslint-disable-next-line\n const { shape, ...optionsWithoutShapeDef } = options\n const allOptions = JSON.stringify(\n optionsWithoutShapeDef,\n Object.keys(options).sort()\n )\n const shapeHash = shapeDef + allOptions\n\n return shapeHash\n}\n\nexport function getShapeStream(options: ShapeStreamOptions): ShapeStream {\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)!\n } else {\n const newShapeStream = new ShapeStream(options)\n\n streamCache.set(shapeHash, newShapeStream)\n\n // Return the created shape\n return newShapeStream\n }\n}\n\nexport function getShape(shapeStream: ShapeStream): Shape {\n // If the stream is already cached, return\n if (shapeCache.has(shapeStream)) {\n // Return the ShapeStream\n return shapeCache.get(shapeStream)!\n } else {\n const newShape = new Shape(shapeStream)\n\n shapeCache.set(shapeStream, newShape)\n\n // Return the created shape\n return newShape\n }\n}\n\ninterface ShapeProviderProps {\n children: React.ReactNode\n}\n\n// Shapes Provider Component\nexport function ShapesProvider({ children }: ShapeProviderProps): JSX.Element {\n // Provide the context value\n return (\n <ShapesContext.Provider value={{ getShape, getShapeStream }}>\n {children}\n </ShapesContext.Provider>\n )\n}\n\nexport function useShapeContext() {\n const context = useContext(ShapesContext)\n if (!context) {\n throw new Error(`useShapeContext must be used within a ShapeProvider`)\n }\n return context\n}\n\ninterface UseShapeResult {\n /**\n * The array of rows that make up the Shape.\n * @type {{ [key: string]: JsonSerializable }[]}\n */\n data: { [key: string]: JsonSerializable }[]\n /**\n * The Shape instance used by this useShape\n * @type(Shape)\n */\n shape: Shape\n error: Shape[`error`]\n isError: boolean\n /**\n * Has the ShapeStream caught up with the replication log from Postgres.\n */\n isUpToDate: boolean\n}\n\nfunction shapeSubscribe(shape: Shape, callback: () => void) {\n const unsubscribe = shape.subscribe(callback)\n return () => {\n unsubscribe()\n }\n}\n\nfunction parseShapeData(shape: Shape): UseShapeResult {\n return {\n data: [...shape.valueSync.values()],\n isUpToDate: shape.isUpToDate,\n isError: shape.error !== false,\n shape,\n error: shape.error,\n }\n}\n\nconst identity = (arg: unknown) => arg\n\ninterface UseShapeOptions<Selection> extends ShapeStreamOptions {\n selector?: (value: UseShapeResult) => Selection\n}\n\nexport function useShape<Selection = UseShapeResult>({\n selector = identity as never,\n ...options\n}: UseShapeOptions<Selection>): Selection {\n const { getShape, getShapeStream } = useShapeContext()\n const shapeStream = getShapeStream(options as ShapeStreamOptions)\n const shape = getShape(shapeStream)\n\n const latestShapeData = useRef(parseShapeData(shape))\n const getSnapshot = React.useCallback(() => latestShapeData.current, [])\n const shapeData = useSyncExternalStoreWithSelector(\n useCallback(\n (onStoreChange) =>\n shapeSubscribe(shape, () => {\n latestShapeData.current = parseShapeData(shape)\n onStoreChange()\n }),\n [shape]\n ),\n getSnapshot,\n getSnapshot,\n selector\n )\n\n return shapeData\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAAA;AAAA,EAEE;AAAA,EACA;AAAA,OAEK;AACP,OAAO,SAAS,eAAe,aAAa,YAAY,cAAc;AACtE,SAAS,wCAAwC;AAQjD,IAAM,gBAAgB,cAAuC,IAAI;AAEjE,IAAM,cAAc,oBAAI,IAAyB;AACjD,IAAM,aAAa,oBAAI,IAAwB;AAE/C,eAAsB,aACpB,SACgB;AAChB,QAAM,cAAc,eAAe,OAAO;AAC1C,QAAM,QAAQ,SAAS,WAAW;AAClC,QAAM,MAAM;AACZ,SAAO;AACT;AAEO,SAAS,kBAAkB,SAAqC;AACrE,QAAM,WAAW,KAAK;AAAA,IACpB,QAAQ;AAAA,IACR,OAAO,KAAK,QAAQ,KAAK,EAAE,KAAK;AAAA,EAClC;AAEA,QAA6C,cAArC,QAnCV,IAmC+C,IAA3B,mCAA2B,IAA3B,CAAV;AACR,QAAM,aAAa,KAAK;AAAA,IACtB;AAAA,IACA,OAAO,KAAK,OAAO,EAAE,KAAK;AAAA,EAC5B;AACA,QAAM,YAAY,WAAW;AAE7B,SAAO;AACT;AAEO,SAAS,eAAe,SAA0C;AACvE,QAAM,YAAY,kBAAkB,OAAO;AAG3C,MAAI,YAAY,IAAI,SAAS,GAAG;AAE9B,WAAO,YAAY,IAAI,SAAS;AAAA,EAClC,OAAO;AACL,UAAM,iBAAiB,IAAI,YAAY,OAAO;AAE9C,gBAAY,IAAI,WAAW,cAAc;AAGzC,WAAO;AAAA,EACT;AACF;AAEO,SAAS,SAAS,aAAiC;AAExD,MAAI,WAAW,IAAI,WAAW,GAAG;AAE/B,WAAO,WAAW,IAAI,WAAW;AAAA,EACnC,OAAO;AACL,UAAM,WAAW,IAAI,MAAM,WAAW;AAEtC,eAAW,IAAI,aAAa,QAAQ;AAGpC,WAAO;AAAA,EACT;AACF;AAOO,SAAS,eAAe,EAAE,SAAS,GAAoC;AAE5E,SACE,oCAAC,cAAc,UAAd,EAAuB,OAAO,EAAE,UAAU,eAAe,KACvD,QACH;AAEJ;AAEO,SAAS,kBAAkB;AAChC,QAAM,UAAU,WAAW,aAAa;AACxC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AACA,SAAO;AACT;AAqBA,SAAS,eAAe,OAAc,UAAsB;AAC1D,QAAM,cAAc,MAAM,UAAU,QAAQ;AAC5C,SAAO,MAAM;AACX,gBAAY;AAAA,EACd;AACF;AAEA,SAAS,eAAe,OAA8B;AACpD,SAAO;AAAA,IACL,MAAM,CAAC,GAAG,MAAM,UAAU,OAAO,CAAC;AAAA,IAClC,YAAY,MAAM;AAAA,IAClB,SAAS,MAAM,UAAU;AAAA,IACzB;AAAA,IACA,OAAO,MAAM;AAAA,EACf;AACF;AAEA,IAAM,WAAW,CAAC,QAAiB;AAM5B,SAAS,SAAqC,IAGX;AAHW,eACnD;AAAA,eAAW;AAAA,EA9Ib,IA6IqD,IAEhD,oBAFgD,IAEhD;AAAA,IADH;AAAA;AAGA,QAAM,EAAE,UAAAA,WAAU,gBAAAC,gBAAe,IAAI,gBAAgB;AACrD,QAAM,cAAcA,gBAAe,OAA6B;AAChE,QAAM,QAAQD,UAAS,WAAW;AAElC,QAAM,kBAAkB,OAAO,eAAe,KAAK,CAAC;AACpD,QAAM,cAAc,MAAM,YAAY,MAAM,gBAAgB,SAAS,CAAC,CAAC;AACvE,QAAM,YAAY;AAAA,IAChB;AAAA,MACE,CAAC,kBACC,eAAe,OAAO,MAAM;AAC1B,wBAAgB,UAAU,eAAe,KAAK;AAC9C,sBAAc;AAAA,MAChB,CAAC;AAAA,MACH,CAAC,KAAK;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO;AACT;","names":["getShape","getShapeStream"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
2
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
3
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
4
|
+
var __objRest = (source, exclude) => {
|
|
5
|
+
var target = {};
|
|
6
|
+
for (var prop in source)
|
|
7
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
8
|
+
target[prop] = source[prop];
|
|
9
|
+
if (source != null && __getOwnPropSymbols)
|
|
10
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
11
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
12
|
+
target[prop] = source[prop];
|
|
13
|
+
}
|
|
14
|
+
return target;
|
|
15
|
+
};
|
|
16
|
+
var __async = (__this, __arguments, generator) => {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
var fulfilled = (value) => {
|
|
19
|
+
try {
|
|
20
|
+
step(generator.next(value));
|
|
21
|
+
} catch (e) {
|
|
22
|
+
reject(e);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var rejected = (value) => {
|
|
26
|
+
try {
|
|
27
|
+
step(generator.throw(value));
|
|
28
|
+
} catch (e) {
|
|
29
|
+
reject(e);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
33
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// src/react-hooks.tsx
|
|
38
|
+
import {
|
|
39
|
+
Shape,
|
|
40
|
+
ShapeStream
|
|
41
|
+
} from "@electric-sql/next";
|
|
42
|
+
import React, { createContext, useCallback, useContext, useRef } from "react";
|
|
43
|
+
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector.js";
|
|
44
|
+
var ShapesContext = createContext(null);
|
|
45
|
+
var streamCache = /* @__PURE__ */ new Map();
|
|
46
|
+
var shapeCache = /* @__PURE__ */ new Map();
|
|
47
|
+
function preloadShape(options) {
|
|
48
|
+
return __async(this, null, function* () {
|
|
49
|
+
const shapeStream = getShapeStream(options);
|
|
50
|
+
const shape = getShape(shapeStream);
|
|
51
|
+
yield shape.value;
|
|
52
|
+
return shape;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function sortedOptionsHash(options) {
|
|
56
|
+
const shapeDef = JSON.stringify(
|
|
57
|
+
options.shape,
|
|
58
|
+
Object.keys(options.shape).sort()
|
|
59
|
+
);
|
|
60
|
+
const _a = options, { shape } = _a, optionsWithoutShapeDef = __objRest(_a, ["shape"]);
|
|
61
|
+
const allOptions = JSON.stringify(
|
|
62
|
+
optionsWithoutShapeDef,
|
|
63
|
+
Object.keys(options).sort()
|
|
64
|
+
);
|
|
65
|
+
const shapeHash = shapeDef + allOptions;
|
|
66
|
+
return shapeHash;
|
|
67
|
+
}
|
|
68
|
+
function getShapeStream(options) {
|
|
69
|
+
const shapeHash = sortedOptionsHash(options);
|
|
70
|
+
if (streamCache.has(shapeHash)) {
|
|
71
|
+
return streamCache.get(shapeHash);
|
|
72
|
+
} else {
|
|
73
|
+
const newShapeStream = new ShapeStream(options);
|
|
74
|
+
streamCache.set(shapeHash, newShapeStream);
|
|
75
|
+
return newShapeStream;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function getShape(shapeStream) {
|
|
79
|
+
if (shapeCache.has(shapeStream)) {
|
|
80
|
+
return shapeCache.get(shapeStream);
|
|
81
|
+
} else {
|
|
82
|
+
const newShape = new Shape(shapeStream);
|
|
83
|
+
shapeCache.set(shapeStream, newShape);
|
|
84
|
+
return newShape;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function ShapesProvider({ children }) {
|
|
88
|
+
return /* @__PURE__ */ React.createElement(ShapesContext.Provider, { value: { getShape, getShapeStream } }, children);
|
|
89
|
+
}
|
|
90
|
+
function useShapeContext() {
|
|
91
|
+
const context = useContext(ShapesContext);
|
|
92
|
+
if (!context) {
|
|
93
|
+
throw new Error(`useShapeContext must be used within a ShapeProvider`);
|
|
94
|
+
}
|
|
95
|
+
return context;
|
|
96
|
+
}
|
|
97
|
+
function shapeSubscribe(shape, callback) {
|
|
98
|
+
const unsubscribe = shape.subscribe(callback);
|
|
99
|
+
return () => {
|
|
100
|
+
unsubscribe();
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function parseShapeData(shape) {
|
|
104
|
+
return {
|
|
105
|
+
data: [...shape.valueSync.values()],
|
|
106
|
+
isUpToDate: shape.isUpToDate,
|
|
107
|
+
isError: shape.error !== false,
|
|
108
|
+
shape,
|
|
109
|
+
error: shape.error
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
var identity = (arg) => arg;
|
|
113
|
+
function useShape(_a) {
|
|
114
|
+
var _b = _a, {
|
|
115
|
+
selector = identity
|
|
116
|
+
} = _b, options = __objRest(_b, [
|
|
117
|
+
"selector"
|
|
118
|
+
]);
|
|
119
|
+
const { getShape: getShape2, getShapeStream: getShapeStream2 } = useShapeContext();
|
|
120
|
+
const shapeStream = getShapeStream2(options);
|
|
121
|
+
const shape = getShape2(shapeStream);
|
|
122
|
+
const latestShapeData = useRef(parseShapeData(shape));
|
|
123
|
+
const getSnapshot = React.useCallback(() => latestShapeData.current, []);
|
|
124
|
+
const shapeData = useSyncExternalStoreWithSelector(
|
|
125
|
+
useCallback(
|
|
126
|
+
(onStoreChange) => shapeSubscribe(shape, () => {
|
|
127
|
+
latestShapeData.current = parseShapeData(shape);
|
|
128
|
+
onStoreChange();
|
|
129
|
+
}),
|
|
130
|
+
[shape]
|
|
131
|
+
),
|
|
132
|
+
getSnapshot,
|
|
133
|
+
getSnapshot,
|
|
134
|
+
selector
|
|
135
|
+
);
|
|
136
|
+
return shapeData;
|
|
137
|
+
}
|
|
138
|
+
export {
|
|
139
|
+
ShapesProvider,
|
|
140
|
+
getShape,
|
|
141
|
+
getShapeStream,
|
|
142
|
+
preloadShape,
|
|
143
|
+
sortedOptionsHash,
|
|
144
|
+
useShape,
|
|
145
|
+
useShapeContext
|
|
146
|
+
};
|
|
147
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react-hooks.tsx"],"sourcesContent":["import {\n JsonSerializable,\n Shape,\n ShapeStream,\n ShapeStreamOptions,\n} from '@electric-sql/next'\nimport React, { createContext, useCallback, useContext, useRef } from 'react'\nimport { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector.js'\n\ninterface ShapeContextType {\n getShape: (shapeStream: ShapeStream) => Shape\n getShapeStream: (options: ShapeStreamOptions) => ShapeStream\n}\n\n// Create a Context\nconst ShapesContext = createContext<ShapeContextType | null>(null)\n\nconst streamCache = new Map<string, ShapeStream>()\nconst shapeCache = new Map<ShapeStream, Shape>()\n\nexport async function preloadShape(\n options: ShapeStreamOptions\n): Promise<Shape> {\n const shapeStream = getShapeStream(options)\n const shape = getShape(shapeStream)\n await shape.value\n return shape\n}\n\nexport function sortedOptionsHash(options: ShapeStreamOptions): string {\n const shapeDef = JSON.stringify(\n options.shape,\n Object.keys(options.shape).sort()\n )\n // eslint-disable-next-line\n const { shape, ...optionsWithoutShapeDef } = options\n const allOptions = JSON.stringify(\n optionsWithoutShapeDef,\n Object.keys(options).sort()\n )\n const shapeHash = shapeDef + allOptions\n\n return shapeHash\n}\n\nexport function getShapeStream(options: ShapeStreamOptions): ShapeStream {\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)!\n } else {\n const newShapeStream = new ShapeStream(options)\n\n streamCache.set(shapeHash, newShapeStream)\n\n // Return the created shape\n return newShapeStream\n }\n}\n\nexport function getShape(shapeStream: ShapeStream): Shape {\n // If the stream is already cached, return\n if (shapeCache.has(shapeStream)) {\n // Return the ShapeStream\n return shapeCache.get(shapeStream)!\n } else {\n const newShape = new Shape(shapeStream)\n\n shapeCache.set(shapeStream, newShape)\n\n // Return the created shape\n return newShape\n }\n}\n\ninterface ShapeProviderProps {\n children: React.ReactNode\n}\n\n// Shapes Provider Component\nexport function ShapesProvider({ children }: ShapeProviderProps): JSX.Element {\n // Provide the context value\n return (\n <ShapesContext.Provider value={{ getShape, getShapeStream }}>\n {children}\n </ShapesContext.Provider>\n )\n}\n\nexport function useShapeContext() {\n const context = useContext(ShapesContext)\n if (!context) {\n throw new Error(`useShapeContext must be used within a ShapeProvider`)\n }\n return context\n}\n\ninterface UseShapeResult {\n /**\n * The array of rows that make up the Shape.\n * @type {{ [key: string]: JsonSerializable }[]}\n */\n data: { [key: string]: JsonSerializable }[]\n /**\n * The Shape instance used by this useShape\n * @type(Shape)\n */\n shape: Shape\n error: Shape[`error`]\n isError: boolean\n /**\n * Has the ShapeStream caught up with the replication log from Postgres.\n */\n isUpToDate: boolean\n}\n\nfunction shapeSubscribe(shape: Shape, callback: () => void) {\n const unsubscribe = shape.subscribe(callback)\n return () => {\n unsubscribe()\n }\n}\n\nfunction parseShapeData(shape: Shape): UseShapeResult {\n return {\n data: [...shape.valueSync.values()],\n isUpToDate: shape.isUpToDate,\n isError: shape.error !== false,\n shape,\n error: shape.error,\n }\n}\n\nconst identity = (arg: unknown) => arg\n\ninterface UseShapeOptions<Selection> extends ShapeStreamOptions {\n selector?: (value: UseShapeResult) => Selection\n}\n\nexport function useShape<Selection = UseShapeResult>({\n selector = identity as never,\n ...options\n}: UseShapeOptions<Selection>): Selection {\n const { getShape, getShapeStream } = useShapeContext()\n const shapeStream = getShapeStream(options as ShapeStreamOptions)\n const shape = getShape(shapeStream)\n\n const latestShapeData = useRef(parseShapeData(shape))\n const getSnapshot = React.useCallback(() => latestShapeData.current, [])\n const shapeData = useSyncExternalStoreWithSelector(\n useCallback(\n (onStoreChange) =>\n shapeSubscribe(shape, () => {\n latestShapeData.current = parseShapeData(shape)\n onStoreChange()\n }),\n [shape]\n ),\n getSnapshot,\n getSnapshot,\n selector\n )\n\n return shapeData\n}\n"],"mappings":"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","names":["getShape","getShapeStream"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@electric-sql/react",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
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://next.electric-sql.com",
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"use-sync-external-store": "^1.2.2",
|
|
34
|
-
"@electric-sql/next": "0.1.
|
|
34
|
+
"@electric-sql/next": "0.1.1"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@testing-library/react": "^16.0.0",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
}
|
|
73
73
|
},
|
|
74
74
|
"scripts": {
|
|
75
|
-
"test": "
|
|
75
|
+
"test": "pnpm exec vitest",
|
|
76
76
|
"typecheck": "tsc -p tsconfig.json",
|
|
77
77
|
"build": "shx rm -rf dist && concurrently \"tsup\" \"tsc -p tsconfig.build.json\"",
|
|
78
78
|
"stylecheck": "eslint . --quiet",
|
package/src/react-hooks.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
ShapeStreamOptions,
|
|
6
6
|
} from '@electric-sql/next'
|
|
7
7
|
import React, { createContext, useCallback, useContext, useRef } from 'react'
|
|
8
|
-
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector'
|
|
8
|
+
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector.js'
|
|
9
9
|
|
|
10
10
|
interface ShapeContextType {
|
|
11
11
|
getShape: (shapeStream: ShapeStream) => Shape
|