@legit-sdk/react 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @legit-sdk/react
2
2
 
3
- Thin React wrapper around `@legit-sdk/core` providing a provider for SDK initialization and a hook to work with file content and history.
3
+ React hooks for `@legit-sdk/core` - local-first file editing with version control.
4
4
 
5
5
  ## Install
6
6
 
@@ -8,32 +8,29 @@ Thin React wrapper around `@legit-sdk/core` providing a provider for SDK initial
8
8
  pnpm add @legit-sdk/core @legit-sdk/react
9
9
  ```
10
10
 
11
- Peer dependencies: `react >=18`.
11
+ **Peer dependencies:** `react >=18`
12
12
 
13
- ## Quick start
14
-
15
- - Wrap your app with `LegitProvider` once.
16
- - Use `useLegitFile(path)` inside components to read/update a file and access history.
13
+ ## Usage
17
14
 
18
15
  ```tsx
19
- import React from 'react';
20
16
  import { LegitProvider, useLegitFile } from '@legit-sdk/react';
21
17
 
22
18
  function Editor() {
23
- const { content, setContent, history, loading, error } =
24
- useLegitFile('/doc.txt');
25
- const [text, setText] = React.useState(content);
26
-
27
- React.useEffect(() => setText(content), [content]);
19
+ const { content, setContent, history, loading } = useLegitFile(
20
+ '/document.txt',
21
+ { initialContent: 'Hello World' } // auto-create if missing
22
+ );
23
+ const [text, setText] = useState(content || '');
28
24
 
29
- if (loading) return <div>Loading…</div>;
30
- if (error) return <div>Error: {error.message}</div>;
25
+ useEffect(() => setText(content || ''), [content]);
31
26
 
32
27
  return (
33
28
  <div>
34
- <input value={text} onChange={e => setText(e.target.value)} />
29
+ <textarea value={text} onChange={e => setText(e.target.value)} />
35
30
  <button onClick={() => setContent(text)}>Save</button>
36
- <div>History entries: {history.length}</div>
31
+ {history.map(h => (
32
+ <div key={h.oid}>{h.message}</div>
33
+ ))}
37
34
  </div>
38
35
  );
39
36
  }
@@ -49,24 +46,56 @@ export default function App() {
49
46
 
50
47
  ## API
51
48
 
52
- - `LegitProvider`: initializes a shared SDK instance and polls repository HEAD.
53
- - `useLegitFile(path: string)` returns:
54
- - `content: string`
55
- - `setContent(newText: string): Promise<void>`
56
- - `history: HistoryItem[]`
57
- - `getPastState(commitHash: string): Promise<string>`
58
- - `loading: boolean`
59
- - `error?: Error`
49
+ ### `LegitProvider`
50
+
51
+ Wraps your app and initializes the SDK instance. Polls HEAD for changes.
52
+
53
+ ```tsx
54
+ <LegitProvider>
55
+ <YourApp />
56
+ </LegitProvider>
57
+ ```
58
+
59
+ ### `useLegitFile(path, options?)`
60
+
61
+ Hook for file operations.
60
62
 
61
- You can also access the provider context via `useLegitContext()` for advanced scenarios.
63
+ **Options:**
64
+
65
+ - `initialContent?: string` - Auto-create file with this content if missing
66
+
67
+ **Returns:**
68
+
69
+ - `content: string | null` - Current file content
70
+ - `setContent(text: string): Promise<void>` - Save and commit
71
+ - `history: HistoryItem[]` - Commit history
72
+ - `getPastState(oid: string): Promise<string>` - Read file at commit
73
+ - `loading: boolean` - Loading state
74
+ - `error?: Error` - Error state
75
+
76
+ ### `useLegitContext()`
77
+
78
+ Access provider context (legitFs, head, loading, error).
62
79
 
63
80
  ## Types
64
81
 
65
- - `UseLegitFileReturn`
66
- - `LegitContextValue`, `LegitProviderProps`
67
- - `HistoryItem` is exported by `@legit-sdk/core` and used in the hook return type.
82
+ ```ts
83
+ interface UseLegitFileOptions {
84
+ initialContent?: string;
85
+ }
86
+
87
+ type UseLegitFileReturn = {
88
+ content: string | null;
89
+ setContent: (text: string) => Promise<void>;
90
+ history: HistoryItem[];
91
+ getPastState: (oid: string) => Promise<string>;
92
+ loading: boolean;
93
+ error?: Error;
94
+ legitFs: LegitFs | null;
95
+ };
96
+ ```
68
97
 
69
- ## Notes
98
+ ## See Also
70
99
 
71
- - This package does not bundle `react` or `@legit-sdk/core`; install them in your app.
72
- - The provider should be mounted once at the root of your React tree.
100
+ - [Starter Example](../../examples/react-starter) - Full working example
101
+ - [Spec Documentation](./spec.md) - Detailed API documentation
package/dist/index.d.ts CHANGED
@@ -14,6 +14,9 @@ interface LegitProviderProps {
14
14
  }
15
15
  declare const LegitProvider: ({ children }: LegitProviderProps) => react_jsx_runtime.JSX.Element;
16
16
 
17
+ interface UseLegitFileOptions {
18
+ initialContent?: string;
19
+ }
17
20
  type UseLegitFileReturn = {
18
21
  content: string | null;
19
22
  setContent: (newText: string) => Promise<void>;
@@ -23,7 +26,7 @@ type UseLegitFileReturn = {
23
26
  error?: Error;
24
27
  legitFs: Awaited<ReturnType<typeof initLegitFs>> | null;
25
28
  };
26
- declare function useLegitFile(path: string): UseLegitFileReturn;
29
+ declare function useLegitFile(path: string, options?: UseLegitFileOptions): UseLegitFileReturn;
27
30
 
28
31
  export { LegitProvider, useLegitContext, useLegitFile };
29
- export type { LegitContextValue, LegitProviderProps, UseLegitFileReturn };
32
+ export type { LegitContextValue, LegitProviderProps, UseLegitFileOptions, UseLegitFileReturn };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // @legit-sdk/react bundle (ESM)
2
2
  // Generated by esbuild
3
3
 
4
- var C=Object.defineProperty;var r=(i,e)=>C(i,"name",{value:e,configurable:!0});import{createContext as E,useContext as R,useEffect as H,useState as c,useRef as S}from"react";import{initLegitFs as I}from"@legit-sdk/core";import b from"memfs";import{jsx as T}from"react/jsx-runtime";var v=E({legitFs:null,loading:!0,head:null}),h=r(()=>R(v),"useLegitContext"),N=r(({children:i})=>{let[e,d]=c(null),[m,u]=c(!0),[g,p]=c(),[y,L]=c(null),l=S(null);return H(()=>{let a=!0,s;return r(async()=>{try{let o=await I(b,"/");return a?(d(o),u(!1),s=setInterval(async()=>{if(o)try{let t=await o.promises.readFile("/.legit/branches/main/.legit/head","utf8");t!==l.current&&(l.current=t,L(t))}catch(t){console.error("Polling head failed:",t)}},200),()=>clearInterval(s)):void 0}catch(o){a&&(p(o),u(!1))}},"initFs")(),()=>{a=!1,s&&clearInterval(s)}},[]),T(v.Provider,{value:{legitFs:e,loading:m,head:y,error:g},children:i})},"LegitProvider");import{useEffect as $,useState as f}from"react";function k(i){let{legitFs:e,error:d,head:m}=h(),[u,g]=f(null),[p,y]=f([]),[L,l]=f(!0),[a,s]=f();return $(()=>{if(!e)return;r(async()=>{l(!0);try{let[n,P]=await Promise.allSettled([e.promises.readFile(`/.legit/branches/main${i}`,"utf8"),e.promises.readFile("/.legit/branches/main/.legit/history","utf8")]),F=null;if(n.status==="fulfilled")F=n.value;else if(n.reason?.code&&n.reason.code==="ENOENT")F=null;else throw n.reason;let x=[];if(P.status==="fulfilled")try{x=JSON.parse(P.value)}catch{x=[]}g(F??null),y(x),s(void 0)}catch(n){s(n)}finally{l(!1)}},"load")()},[e,i,m]),{content:u,setContent:r(async t=>{e&&(await e.promises.writeFile(`/.legit/branches/main${i}`,t,"utf8"),g(t))},"save"),history:p,getPastState:r(async t=>{if(!e)return"";try{return await e.promises.readFile(`/.legit/commits/${t.slice(0,2)}/${t.slice(2)}${i}`,"utf8")}catch{return""}},"getPastState"),loading:L,error:a??d,legitFs:e}}r(k,"useLegitFile");export{N as LegitProvider,h as useLegitContext,k as useLegitFile};
4
+ var T=Object.defineProperty;var s=(i,l)=>T(i,"name",{value:l,configurable:!0});import{createContext as U,useContext as z,useEffect as A,useState as F,useRef as $}from"react";import{initLegitFs as V}from"@legit-sdk/core";import k from"memfs";import{jsx as D}from"react/jsx-runtime";var N=U({legitFs:null,loading:!0,head:null}),E=s(()=>z(N),"useLegitContext"),_=500,J=s(({children:i})=>{let[l,e]=F(null),[x,y]=F(!0),[f,g]=F(),[P,m]=F(null),a=$(null);return A(()=>{let o=!0,d,u="";return s(async()=>{try{let n=await V(k,"/");if(!o)return;e(n),y(!1),d=setInterval(async()=>{if(!(!o||!n))try{let t=await n.promises.readFile("/.legit/branches/main/.legit/head","utf8");t!==u&&t!==a.current&&(u=t,a.current=t,m(t))}catch(t){o&&t?.code!=="ENOENT"&&console.error("Polling head failed:",t)}},_)}catch(n){o&&(g(n),y(!1))}},"initFs")(),()=>{o=!1,d&&clearInterval(d)}},[]),D(N.Provider,{value:{legitFs:l,loading:x,head:P,error:f},children:i})},"LegitProvider");import{useEffect as b,useState as h,useRef as R,useCallback as M}from"react";function q(i,l){let{legitFs:e,error:x,head:y}=E(),[f,g]=h(null),[P,m]=h([]),[a,o]=h(!0),[d,u]=h(),p=R(null),n=R(!1),t=R(!1);b(()=>{if(!e)return;let r=!1;return s(async()=>{o(!0);try{let L=`/.legit/branches/main${i}`,O="/.legit/branches/main/.legit/history",[v,w]=await Promise.allSettled([e.promises.readFile(L,"utf8"),e.promises.readFile(O,"utf8").catch(()=>"")]);if(r)return;let H=v.status==="fulfilled"?v.value:null,S=[];if(w.status==="fulfilled")try{let I=w.value;I&&(S=JSON.parse(I))}catch{}p.current===H&&(p.current=null),g(H),m(S),u(void 0)}catch(L){L?.code!=="ENOENT"?u(L):(g(null),m([]))}finally{r||o(!1)}},"load")(),()=>{r=!0}},[e,i,y]);let C=M(async r=>{if(e)try{p.current=r,await e.promises.writeFile(`/.legit/branches/main${i}`,r,"utf8"),g(r)}catch(c){throw p.current=null,u(c),c}},[e,i]);return b(()=>{n.current||t.current||!a&&f===null&&e&&l?.initialContent&&(t.current=!0,n.current=!0,s(async()=>{try{await C(l.initialContent)}catch(c){console.error("Failed to initialize file:",c),n.current=!1}finally{t.current=!1}},"initialize")())},[a,f,e,l?.initialContent,C]),{content:f,setContent:C,history:P,getPastState:s(async r=>{if(!e)return"";try{return await e.promises.readFile(`/.legit/commits/${r.slice(0,2)}/${r.slice(2)}${i}`,"utf8")}catch{return""}},"getPastState"),loading:a,error:d??x,legitFs:e}}s(q,"useLegitFile");export{J as LegitProvider,E as useLegitContext,q as useLegitFile};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legit-sdk/react",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "React wrapper for @legit-sdk/core",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",