@arcote.tech/arc-react 0.0.20 → 0.0.22

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,43 +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
- const deserialized = collection.deserialize(e.target.result);
50
- resolve(deserialized);
59
+ resolve(e.target.result);
51
60
  };
52
61
  });
53
62
  }
54
- async findByIndex(collection, index, data) {
63
+ async findByIndex(store, index, data) {
55
64
  return new Promise((resolve) => {
56
- const idbIndex = this.transaction.objectStore(collection.name).index(index);
65
+ const idbIndex = this.transaction.objectStore(store).index(index);
57
66
  const value = idbIndex.keyPath.map((key) => data[key]);
58
67
  const keyRange = IDBKeyRange.only(value);
59
68
  const result = idbIndex.getAll(keyRange);
60
69
  result.onsuccess = (e) => {
61
- const deserialized = e.target.result.map((e2) => collection.deserialize(e2));
62
- resolve(deserialized);
70
+ resolve(e.target.result);
63
71
  };
64
72
  });
65
73
  }
66
- async findAll(collection) {
74
+ async findAll(store) {
67
75
  return new Promise((resolve) => {
68
- const result = this.transaction.objectStore(collection.name).getAll();
76
+ const result = this.transaction.objectStore(store).getAll();
69
77
  result.onsuccess = (e) => {
70
- const deserialized = e.target.result.map((e2) => collection.deserialize(e2));
71
- resolve(deserialized);
78
+ resolve(e.target.result);
72
79
  };
73
80
  });
74
81
  }
75
82
  }
76
83
 
77
84
  class IDBReadWriteTransaction extends IDBReadTransaction {
78
- async set(collection, data) {
85
+ async set(store, data) {
79
86
  return new Promise((resolve, reject) => {
80
- const serialized = collection.serialize(data);
81
- const result = this.transaction.objectStore(collection.name).put(serialized);
87
+ const result = this.transaction.objectStore(store).put(data);
82
88
  result.onsuccess = (e) => {
83
89
  resolve();
84
90
  };
@@ -87,9 +93,9 @@ class IDBReadWriteTransaction extends IDBReadTransaction {
87
93
  };
88
94
  });
89
95
  }
90
- async remove(collection, id) {
96
+ async remove(store, id) {
91
97
  return new Promise((resolve, reject) => {
92
- const result = this.transaction.objectStore(collection.name).delete(id);
98
+ const result = this.transaction.objectStore(store).delete(id);
93
99
  result.onsuccess = (e) => {
94
100
  resolve();
95
101
  };
@@ -113,68 +119,118 @@ class IDBAdapter {
113
119
  constructor(db) {
114
120
  this.db = db;
115
121
  }
116
- readWriteTransaction(collections) {
117
- 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");
118
126
  return new IDBReadWriteTransaction(transaction);
119
127
  }
120
- readTransaction(collections) {
121
- 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");
122
132
  return new IDBReadTransaction(transaction);
123
133
  }
124
134
  }
125
135
  // reactModel.tsx
126
- import { createContext, useContext, useEffect, useState } from "react";
136
+ import {
137
+ MasterDataStorage,
138
+ rtcClientFactory
139
+ } from "@arcote.tech/arc";
140
+ import { createContext, useContext, useEffect, useRef, useState } from "react";
127
141
  import { jsx } from "react/jsx-runtime";
128
- var reactModel = (model) => {
142
+ var reactModel = (arcContext, databaseName) => {
129
143
  const LiveModelContext = createContext(null);
144
+ const LocalModelContext = createContext(null);
145
+ let dataStorage = null;
130
146
  return [
131
- function LiveModelProvider(props) {
147
+ function LiveModelProvider({ children }) {
148
+ const dbAdapterPromise = idbAdapterFactory(databaseName, arcContext.version)(arcContext);
149
+ dataStorage = new MasterDataStorage(dbAdapterPromise, rtcClientFactory, arcContext);
132
150
  return /* @__PURE__ */ jsx(LiveModelContext.Provider, {
133
- value: {
134
- model
135
- },
136
- children: props.children
151
+ value: { dataStorage, dbAdapterPromise },
152
+ children
153
+ }, undefined, false, undefined, this);
154
+ },
155
+ function LocalModelProvider({ children }) {
156
+ const parentContext = useContext(LiveModelContext);
157
+ if (!parentContext) {
158
+ throw new Error("LocalModelProvider must be used within a LiveModelProvider");
159
+ }
160
+ const [localContext] = useState(() => ({
161
+ dbAdapterPromise: parentContext.dbAdapterPromise,
162
+ dataStorage: parentContext.dataStorage.fork()
163
+ }));
164
+ return /* @__PURE__ */ jsx(LocalModelContext.Provider, {
165
+ value: localContext,
166
+ children
137
167
  }, undefined, false, undefined, this);
138
168
  },
139
- function useQuery(queryBuilder, dependencies = []) {
140
- const context = useContext(LiveModelContext);
141
- if (!context)
142
- throw new Error("useQuery must be used within a LiveModelProvider");
143
- if (!context.model)
144
- throw new Error("model not found");
169
+ function useQuery(queryBuilderFn, dependencies = []) {
170
+ const context = useContext(LocalModelContext) || useContext(LiveModelContext);
171
+ if (!context) {
172
+ throw new Error("useQuery must be used within a ModelProvider");
173
+ }
145
174
  const [result, setResult] = useState(null);
146
175
  const [loading, setLoading] = useState(true);
176
+ const queryRef = useRef(null);
147
177
  useEffect(() => {
148
- const query = context.model.query(queryBuilder);
149
- query.result$.subscribe((result2) => {
178
+ const queryBuilder = arcContext.queryBuilder();
179
+ const query = queryBuilderFn(queryBuilder).toQuery();
180
+ queryRef.current = query;
181
+ const runQuery = async () => {
182
+ const result2 = await query.run(context.dataStorage, (newResult) => {
183
+ console.log("newResult", newResult);
184
+ setResult(newResult);
185
+ setLoading(false);
186
+ });
150
187
  setResult(result2);
151
188
  setLoading(false);
152
- });
189
+ };
190
+ runQuery();
153
191
  return () => {
154
- query.unsubscribe();
192
+ queryRef.current?.unsubscribe();
193
+ queryRef.current = null;
155
194
  };
156
- }, [context.model, ...dependencies]);
195
+ }, [context, ...dependencies]);
157
196
  return [result, loading];
158
197
  },
159
198
  function useCommands() {
160
- const context = useContext(LiveModelContext);
161
- if (!context)
162
- throw new Error("useCommands must be used within a LiveModelProvider");
163
- return context.model.commands();
164
- },
165
- function query(queryBuilder) {
166
- return new Promise((resolve, reject) => {
167
- const query = model.query(queryBuilder);
168
- const subscription = query.result$.subscribe({
169
- next: (result) => {
170
- subscription.unsubscribe();
171
- resolve(result);
172
- },
173
- error: (error) => {
174
- reject(error);
199
+ const context = useContext(LocalModelContext) || useContext(LiveModelContext);
200
+ if (!context) {
201
+ throw new Error("useQuery must be used within a ModelProvider");
202
+ }
203
+ const commands = arcContext.commands;
204
+ return new Proxy({}, {
205
+ get: (_, name) => {
206
+ if (name in commands) {
207
+ return async (...args) => {
208
+ const dataStorage2 = context.dataStorage.fork();
209
+ const commandContext = arcContext.commandContext(dataStorage2);
210
+ const result = await commands[name](commandContext, ...args);
211
+ await dataStorage2.merge();
212
+ return result;
213
+ };
175
214
  }
176
- });
215
+ console.warn(`Command '${name}' not found in the context.`);
216
+ return;
217
+ }
177
218
  });
219
+ },
220
+ async function query(queryBuilderFn) {
221
+ const queryBuilder = arcContext.queryBuilder();
222
+ const query = queryBuilderFn(queryBuilder).toQuery();
223
+ if (!dataStorage)
224
+ throw new Error("dataStorage not found");
225
+ const result = await query.run(dataStorage);
226
+ return result;
227
+ },
228
+ function useLocalDataStorage() {
229
+ const context = useContext(LocalModelContext);
230
+ if (!context) {
231
+ throw new Error("hook must be used within a ModelProvider");
232
+ }
233
+ return context.dataStorage;
178
234
  }
179
235
  ];
180
236
  };
@@ -184,4 +240,4 @@ export {
184
240
  formResolver
185
241
  };
186
242
 
187
- //# debugId=BF36AB53518853B964756E2164756E21
243
+ //# debugId=9B2592D89513AAF264756E2164756E21
@@ -1,4 +1,7 @@
1
- import { type ArcModelAny, type GetAnyCollectionQueryResult, type QueryFactoryFunction } 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 [({ children }: {
3
3
  children: React.ReactNode;
4
- }) => 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>];
4
+ }) => import("react/jsx-dev-runtime").JSX.Element, ({ children }: {
5
+ children: React.ReactNode;
6
+ }) => 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];
7
+ //# 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.20",
7
+ "version": "0.0.22",
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"