@arcote.tech/arc-react 0.0.21 → 0.0.23

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/form.d.ts CHANGED
@@ -3,3 +3,4 @@ export declare function formResolver(schema: ArcObjectAny): (data: any) => Promi
3
3
  values: any;
4
4
  errors: {};
5
5
  }>;
6
+ //# sourceMappingURL=form.d.ts.map
package/dist/idb.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  import { type DBAdapterFactory } from "@arcote.tech/arc";
2
2
  export declare const idbAdapterFactory: (name: string, version: number) => DBAdapterFactory;
3
+ //# sourceMappingURL=idb.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from "./form.ts";
2
2
  export * from "./idb.ts";
3
3
  export * from "./reactModel.tsx";
4
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -8,6 +8,10 @@ function formResolver(schema) {
8
8
  };
9
9
  }
10
10
  // idb.ts
11
+ import {
12
+ ArcCollection,
13
+ ArcIndexedCollection
14
+ } from "@arcote.tech/arc";
11
15
  var idbAdapterFactory = (name, version) => (context) => new Promise((resolve, reject) => {
12
16
  const dbRequest = indexedDB.open(name, version);
13
17
  dbRequest.addEventListener("error", (err) => {
@@ -18,7 +22,7 @@ var idbAdapterFactory = (name, version) => (context) => new Promise((resolve, re
18
22
  });
19
23
  dbRequest.addEventListener("upgradeneeded", (ev) => {
20
24
  const db = ev.target.result;
21
- context.collections.forEach((collection) => {
25
+ context.elements.filter((element) => element instanceof ArcIndexedCollection || element instanceof ArcCollection).forEach((collection) => {
22
26
  const name2 = collection.name;
23
27
  if (db.objectStoreNames.contains(name2)) {
24
28
  db.deleteObjectStore(collection.name);
@@ -26,7 +30,7 @@ var idbAdapterFactory = (name, version) => (context) => new Promise((resolve, re
26
30
  const objectStore = db.createObjectStore(collection.name, {
27
31
  keyPath: "_id"
28
32
  });
29
- if (collection.indexes) {
33
+ if (collection instanceof ArcIndexedCollection) {
30
34
  Object.entries(collection.indexes).forEach(([name3, keyPath]) => {
31
35
  objectStore.createIndex(name3, keyPath, {
32
36
  unique: false
@@ -34,6 +38,12 @@ var idbAdapterFactory = (name, version) => (context) => new Promise((resolve, re
34
38
  });
35
39
  }
36
40
  });
41
+ if (db.objectStoreNames.contains("state")) {
42
+ db.deleteObjectStore("state");
43
+ }
44
+ db.createObjectStore("state", {
45
+ keyPath: "_id"
46
+ });
37
47
  });
38
48
  });
39
49
 
@@ -42,45 +52,39 @@ class IDBReadTransaction {
42
52
  constructor(transaction) {
43
53
  this.transaction = transaction;
44
54
  }
45
- async findById(collection, id) {
55
+ async findById(store, id) {
46
56
  return new Promise((resolve) => {
47
- const result = this.transaction.objectStore(collection.name).get(id);
57
+ const result = this.transaction.objectStore(store).get(id);
48
58
  result.onsuccess = (e) => {
49
- if (!e.target.result)
50
- return resolve(undefined);
51
- const deserialized = collection.deserialize(e.target.result);
52
- resolve(deserialized);
59
+ resolve(e.target.result);
53
60
  };
54
61
  });
55
62
  }
56
- async findByIndex(collection, index, data) {
63
+ async findByIndex(store, index, data) {
57
64
  return new Promise((resolve) => {
58
- const idbIndex = this.transaction.objectStore(collection.name).index(index);
65
+ const idbIndex = this.transaction.objectStore(store).index(index);
59
66
  const value = idbIndex.keyPath.map((key) => data[key]);
60
67
  const keyRange = IDBKeyRange.only(value);
61
68
  const result = idbIndex.getAll(keyRange);
62
69
  result.onsuccess = (e) => {
63
- const deserialized = e.target.result.map((e2) => collection.deserialize(e2));
64
- resolve(deserialized);
70
+ resolve(e.target.result);
65
71
  };
66
72
  });
67
73
  }
68
- async findAll(collection) {
74
+ async findAll(store) {
69
75
  return new Promise((resolve) => {
70
- const result = this.transaction.objectStore(collection.name).getAll();
76
+ const result = this.transaction.objectStore(store).getAll();
71
77
  result.onsuccess = (e) => {
72
- const deserialized = e.target.result.map((e2) => collection.deserialize(e2));
73
- resolve(deserialized);
78
+ resolve(e.target.result);
74
79
  };
75
80
  });
76
81
  }
77
82
  }
78
83
 
79
84
  class IDBReadWriteTransaction extends IDBReadTransaction {
80
- async set(collection, data) {
85
+ async set(store, data) {
81
86
  return new Promise((resolve, reject) => {
82
- const serialized = collection.serialize(data);
83
- const result = this.transaction.objectStore(collection.name).put(serialized);
87
+ const result = this.transaction.objectStore(store).put(data);
84
88
  result.onsuccess = (e) => {
85
89
  resolve();
86
90
  };
@@ -89,9 +93,9 @@ class IDBReadWriteTransaction extends IDBReadTransaction {
89
93
  };
90
94
  });
91
95
  }
92
- async remove(collection, id) {
96
+ async remove(store, id) {
93
97
  return new Promise((resolve, reject) => {
94
- const result = this.transaction.objectStore(collection.name).delete(id);
98
+ const result = this.transaction.objectStore(store).delete(id);
95
99
  result.onsuccess = (e) => {
96
100
  resolve();
97
101
  };
@@ -115,98 +119,130 @@ class IDBAdapter {
115
119
  constructor(db) {
116
120
  this.db = db;
117
121
  }
118
- readWriteTransaction(collections) {
119
- const transaction = this.db.transaction(collections.map((c) => c.name), "readwrite");
122
+ readWriteTransaction(stores) {
123
+ if (!stores)
124
+ stores = Array.from(this.db.objectStoreNames);
125
+ const transaction = this.db.transaction(stores, "readwrite");
120
126
  return new IDBReadWriteTransaction(transaction);
121
127
  }
122
- readTransaction(collections) {
123
- const transaction = this.db.transaction(collections.map((c) => c.name), "readonly");
128
+ readTransaction(stores) {
129
+ if (!stores)
130
+ stores = Array.from(this.db.objectStoreNames);
131
+ const transaction = this.db.transaction(stores, "readonly");
124
132
  return new IDBReadTransaction(transaction);
125
133
  }
126
134
  }
127
135
  // reactModel.tsx
128
136
  import {
129
- ArcSubModel
137
+ MasterDataStorage,
138
+ rtcClientFactory
130
139
  } from "@arcote.tech/arc";
131
- import { createContext, useContext, useEffect, useState } from "react";
140
+ import { createContext, useContext, useEffect, useRef, useState } from "react";
132
141
  import { jsx } from "react/jsx-runtime";
133
- var reactModel = (model) => {
142
+ var reactModel = (arcContext, databaseName) => {
134
143
  const LiveModelContext = createContext(null);
135
144
  const LocalModelContext = createContext(null);
145
+ let dataStorage = null;
136
146
  return [
137
147
  function LiveModelProvider(props) {
148
+ const dbAdapterPromise = idbAdapterFactory(databaseName, arcContext.version)(arcContext);
149
+ dataStorage = new MasterDataStorage(dbAdapterPromise, rtcClientFactory, arcContext);
150
+ const [syncProgress, setSyncProgress] = useState([]);
151
+ const [syncDone, setSyncDone] = useState(false);
152
+ useEffect(() => {
153
+ const sync = async () => {
154
+ await dataStorage?.sync(({ store, size }) => {
155
+ setSyncProgress((prev) => [...prev, { store, size }]);
156
+ });
157
+ setSyncDone(true);
158
+ };
159
+ sync();
160
+ }, []);
138
161
  return /* @__PURE__ */ jsx(LiveModelContext.Provider, {
139
- value: {
140
- model
141
- },
142
- children: props.children
162
+ value: { dataStorage, dbAdapterPromise },
163
+ children: syncDone ? props.children : /* @__PURE__ */ jsx(props.syncView, {
164
+ progress: syncProgress
165
+ }, undefined, false, undefined, this)
143
166
  }, undefined, false, undefined, this);
144
167
  },
145
- function LocalModelProvider(props) {
146
- const liveModelContext = useContext(LiveModelContext);
147
- if (!liveModelContext) {
168
+ function LocalModelProvider({ children }) {
169
+ const parentContext = useContext(LiveModelContext);
170
+ if (!parentContext) {
148
171
  throw new Error("LocalModelProvider must be used within a LiveModelProvider");
149
172
  }
150
- const [subModel] = useState(() => new ArcSubModel(liveModelContext.model));
173
+ const [localContext] = useState(() => ({
174
+ dbAdapterPromise: parentContext.dbAdapterPromise,
175
+ dataStorage: parentContext.dataStorage.fork()
176
+ }));
151
177
  return /* @__PURE__ */ jsx(LocalModelContext.Provider, {
152
- value: {
153
- model: subModel
154
- },
155
- children: props.children
178
+ value: localContext,
179
+ children
156
180
  }, undefined, false, undefined, this);
157
181
  },
158
- function useQuery(queryBuilder, dependencies = []) {
159
- const localContext = useContext(LocalModelContext);
160
- const liveContext = useContext(LiveModelContext);
161
- const context = localContext || liveContext;
162
- if (!context)
163
- throw new Error("useQuery must be used within a LiveModelProvider or LocalModelProvider");
182
+ function useQuery(queryBuilderFn, dependencies = []) {
183
+ const context = useContext(LocalModelContext) || useContext(LiveModelContext);
184
+ if (!context) {
185
+ throw new Error("useQuery must be used within a ModelProvider");
186
+ }
164
187
  const [result, setResult] = useState(null);
165
188
  const [loading, setLoading] = useState(true);
189
+ const queryRef = useRef(null);
166
190
  useEffect(() => {
167
- const query = context.model.query(queryBuilder);
168
- if (localContext)
169
- console.log("local query", query);
170
- query.result$.subscribe((result2) => {
191
+ const queryBuilder = arcContext.queryBuilder();
192
+ const query = queryBuilderFn(queryBuilder).toQuery();
193
+ queryRef.current = query;
194
+ const runQuery = async () => {
195
+ const result2 = await query.run(context.dataStorage, (newResult) => {
196
+ setResult(newResult);
197
+ setLoading(false);
198
+ });
171
199
  setResult(result2);
172
200
  setLoading(false);
173
- if (localContext)
174
- console.log("local query result change", result2);
175
- });
201
+ };
202
+ runQuery();
176
203
  return () => {
177
- query.unsubscribe();
204
+ queryRef.current?.unsubscribe();
205
+ queryRef.current = null;
178
206
  };
179
- }, [context.model, ...dependencies]);
207
+ }, [context, ...dependencies]);
180
208
  return [result, loading];
181
209
  },
182
210
  function useCommands() {
183
- const localContext = useContext(LocalModelContext);
184
- const liveContext = useContext(LiveModelContext);
185
- const context = localContext || liveContext;
186
- if (!context)
187
- throw new Error("useCommands must be used within a LiveModelProvider or LocalModelProvider");
188
- return context.model.commands();
189
- },
190
- function query(queryBuilder) {
191
- return new Promise((resolve, reject) => {
192
- const query = model.query(queryBuilder);
193
- const subscription = query.result$.subscribe({
194
- next: (result) => {
195
- subscription.unsubscribe();
196
- resolve(result);
197
- },
198
- error: (error) => {
199
- reject(error);
211
+ const context = useContext(LocalModelContext) || useContext(LiveModelContext);
212
+ if (!context) {
213
+ throw new Error("useQuery must be used within a ModelProvider");
214
+ }
215
+ const commands = arcContext.commands;
216
+ return new Proxy({}, {
217
+ get: (_, name) => {
218
+ if (name in commands) {
219
+ return async (...args) => {
220
+ const dataStorage2 = context.dataStorage.fork();
221
+ const commandContext = arcContext.commandContext(dataStorage2);
222
+ const result = await commands[name](commandContext, ...args);
223
+ await dataStorage2.merge();
224
+ return result;
225
+ };
200
226
  }
201
- });
227
+ console.warn(`Command '${name}' not found in the context.`);
228
+ return;
229
+ }
202
230
  });
203
231
  },
204
- function useLocalModel() {
205
- const localContext = useContext(LocalModelContext);
206
- if (!localContext) {
207
- throw new Error("useLocalModel must be used within a LocalModelProvider");
232
+ async function query(queryBuilderFn) {
233
+ const queryBuilder = arcContext.queryBuilder();
234
+ const query = queryBuilderFn(queryBuilder).toQuery();
235
+ if (!dataStorage)
236
+ throw new Error("dataStorage not found");
237
+ const result = await query.run(dataStorage);
238
+ return result;
239
+ },
240
+ function useLocalDataStorage() {
241
+ const context = useContext(LocalModelContext);
242
+ if (!context) {
243
+ throw new Error("hook must be used within a ModelProvider");
208
244
  }
209
- return localContext.model;
245
+ return context.dataStorage;
210
246
  }
211
247
  ];
212
248
  };
@@ -216,4 +252,4 @@ export {
216
252
  formResolver
217
253
  };
218
254
 
219
- //# debugId=9C5FFF223A0A726064756E2164756E21
255
+ //# debugId=4A9F9D29F4CCB1FA64756E2164756E21
@@ -1,6 +1,13 @@
1
- import { type ArcModelAny, type GetAnyCollectionQueryResult, type QueryFactoryFunction, ArcSubModel } from "@arcote.tech/arc";
2
- export declare const reactModel: <Model extends ArcModelAny>(model: Model) => readonly [(props: {
1
+ import { ForkedDataStorage, type ArcContextAny, type CommandsClient, type QueryBuilderFunctionResult, type QueryFactoryFunction } from "@arcote.tech/arc";
2
+ export declare const reactModel: <C extends ArcContextAny>(arcContext: C, databaseName: string) => readonly [(props: {
3
3
  children: React.ReactNode;
4
- }) => import("react/jsx-dev-runtime").JSX.Element, (props: {
4
+ syncView: React.ComponentType<{
5
+ progress: {
6
+ store: string;
7
+ size: number;
8
+ }[];
9
+ }>;
10
+ }) => import("react/jsx-dev-runtime").JSX.Element, ({ children }: {
5
11
  children: React.ReactNode;
6
- }) => import("react/jsx-dev-runtime").JSX.Element, <QueryBuilderFn extends QueryFactoryFunction<Model["context"]>>(queryBuilder: QueryBuilderFn, dependencies?: any[]) => [GetAnyCollectionQueryResult<QueryBuilderFn>, false] | [null, true], () => ReturnType<Model["commands"]>, <QueryBuilderFn extends QueryFactoryFunction<Model["context"]>>(queryBuilder: QueryBuilderFn) => Promise<GetAnyCollectionQueryResult<QueryBuilderFn> | null>, () => ArcSubModel<Model["context"]>];
12
+ }) => import("react/jsx-dev-runtime").JSX.Element, <QueryBuilderFn extends QueryFactoryFunction<C>>(queryBuilderFn: QueryBuilderFn, dependencies?: any[]) => [QueryBuilderFunctionResult<QueryBuilderFn>, boolean], () => CommandsClient<C["commands"]>, <QueryBuilderFn extends QueryFactoryFunction<C>>(queryBuilderFn: QueryBuilderFn) => Promise<QueryBuilderFunctionResult<QueryBuilderFn>>, () => ForkedDataStorage];
13
+ //# sourceMappingURL=reactModel.d.ts.map
package/package.json CHANGED
@@ -4,13 +4,13 @@
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
7
- "version": "0.0.21",
7
+ "version": "0.0.23",
8
8
  "private": false,
9
9
  "author": "Przemysław Krasiński [arcote.tech]",
10
10
  "description": "React client for the Arc framework, providing utilities for querying data and executing commands, enhancing the development of reactive and efficient user interfaces.",
11
11
  "scripts": {
12
12
  "build": "rm -rf dist && bun run ./build.ts && bun run build:declaration",
13
- "build:declaration": "tsc --emitDeclarationOnly --project tsconfig.types.json",
13
+ "build:declaration": "tsc --emitDeclarationOnly --project tsconfig.types.json --declarationMap",
14
14
  "postbuild": "rimraf tsconfig.types.tsbuildinfo",
15
15
  "type-check": "tsc",
16
16
  "dev": "nodemon --ignore dist -e ts,tsx --exec 'bun run build'"
@@ -18,15 +18,16 @@
18
18
  "dependencies": {
19
19
  "@arcote.tech/arc": "latest",
20
20
  "react": "^18.3.1",
21
- "rxjs": "^7.8.1"
21
+ "react-dom": "^18.3.1"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/bun": "latest",
25
25
  "@types/react": "^18.3.5",
26
+ "@types/react-dom": "^18.3.1",
27
+ "nodemon": "^2.0.22",
26
28
  "prettier": "^3.0.3",
27
29
  "rimraf": "^5.0.5",
28
- "typescript": "^5.2.2",
29
- "nodemon": "^2.0.22"
30
+ "typescript": "^5.2.2"
30
31
  },
31
32
  "peerDependencies": {
32
33
  "typescript": "^5.0.0"