@nditure/gania 0.0.0

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/index.cjs ADDED
@@ -0,0 +1,377 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Gania: () => Gania,
24
+ create: () => create,
25
+ default: () => index_default
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/cache/gania-cache.ts
30
+ var GaniaCache = class {
31
+ constructor(dbName, version = 1) {
32
+ this.dbName = dbName;
33
+ this.version = version;
34
+ }
35
+ dbName;
36
+ version;
37
+ db = null;
38
+ ramCache = /* @__PURE__ */ new Map();
39
+ hydratedStores = /* @__PURE__ */ new Set();
40
+ activeHydrations = /* @__PURE__ */ new Map();
41
+ channel = null;
42
+ getDatabase() {
43
+ if (!this.db) {
44
+ throw new Error("GaniaCache has not been initialized.");
45
+ }
46
+ return this.db;
47
+ }
48
+ getStoreMap(store) {
49
+ const map = this.ramCache.get(store);
50
+ if (!map) {
51
+ throw new Error(`Store "${String(store)}" is not initialized.`);
52
+ }
53
+ return map;
54
+ }
55
+ async init(storeNames) {
56
+ return new Promise((resolve, reject) => {
57
+ const request = indexedDB.open(this.dbName, this.version);
58
+ request.onupgradeneeded = () => {
59
+ const db = request.result;
60
+ for (const storeName of storeNames) {
61
+ if (!db.objectStoreNames.contains(storeName)) {
62
+ db.createObjectStore(storeName);
63
+ }
64
+ }
65
+ };
66
+ request.onsuccess = () => {
67
+ this.db = request.result;
68
+ for (const storeName of storeNames) {
69
+ this.ramCache.set(storeName, /* @__PURE__ */ new Map());
70
+ }
71
+ this.channel = new BroadcastChannel(`gania-cache:${this.dbName}`);
72
+ this.channel.onmessage = (event) => this.handleBroadcast(event.data);
73
+ resolve(this);
74
+ };
75
+ request.onerror = () => reject(request.error);
76
+ });
77
+ }
78
+ handleBroadcast(event) {
79
+ const storeMap = this.ramCache.get(event.store);
80
+ if (!storeMap) return;
81
+ switch (event.type) {
82
+ case "set":
83
+ storeMap.set(event.key, event.value);
84
+ break;
85
+ case "delete":
86
+ storeMap.delete(event.key);
87
+ break;
88
+ case "clear":
89
+ storeMap.clear();
90
+ break;
91
+ }
92
+ }
93
+ broadcast(event) {
94
+ this.channel?.postMessage(event);
95
+ }
96
+ async performHydration(store) {
97
+ const db = this.getDatabase();
98
+ const tx = db.transaction(store, "readonly");
99
+ const objectStore = tx.objectStore(store);
100
+ const [keys, values] = await Promise.all([
101
+ new Promise((resolve, reject) => {
102
+ const req = objectStore.getAllKeys();
103
+ req.onsuccess = () => resolve(req.result);
104
+ req.onerror = () => reject(req.error);
105
+ }),
106
+ new Promise((resolve, reject) => {
107
+ const req = objectStore.getAll();
108
+ req.onsuccess = () => resolve(req.result);
109
+ req.onerror = () => reject(req.error);
110
+ })
111
+ ]);
112
+ const storeMap = this.getStoreMap(store);
113
+ keys.forEach((key, index) => {
114
+ storeMap.set(
115
+ key,
116
+ values[index]
117
+ );
118
+ });
119
+ this.hydratedStores.add(store);
120
+ }
121
+ async hydrateStore(store) {
122
+ if (this.hydratedStores.has(store)) {
123
+ return;
124
+ }
125
+ const activeHydration = this.activeHydrations.get(store);
126
+ if (activeHydration) {
127
+ return activeHydration;
128
+ }
129
+ const hydrationPromise = this.performHydration(store);
130
+ this.activeHydrations.set(store, hydrationPromise);
131
+ try {
132
+ await hydrationPromise;
133
+ } finally {
134
+ this.activeHydrations.delete(store);
135
+ }
136
+ }
137
+ async get(store, key) {
138
+ const storeMap = this.getStoreMap(store);
139
+ if (storeMap.has(key)) {
140
+ return storeMap.get(key) ?? null;
141
+ }
142
+ const db = this.getDatabase();
143
+ return new Promise((resolve) => {
144
+ const tx = db.transaction(store, "readonly");
145
+ const request = tx.objectStore(store).get(key);
146
+ request.onsuccess = () => {
147
+ const value = request.result ?? null;
148
+ if (value !== null) {
149
+ storeMap.set(key, value);
150
+ }
151
+ resolve(value);
152
+ };
153
+ request.onerror = () => resolve(null);
154
+ });
155
+ }
156
+ async set(store, key, value) {
157
+ const storeMap = this.getStoreMap(store);
158
+ storeMap.set(key, value);
159
+ const db = this.getDatabase();
160
+ await new Promise((resolve, reject) => {
161
+ const tx = db.transaction(store, "readwrite");
162
+ const request = tx.objectStore(store).put(value, key);
163
+ request.onsuccess = () => resolve();
164
+ request.onerror = () => reject(request.error);
165
+ });
166
+ this.broadcast({
167
+ type: "set",
168
+ store: String(store),
169
+ key,
170
+ value
171
+ });
172
+ }
173
+ async delete(store, key) {
174
+ this.getStoreMap(store).delete(key);
175
+ const db = this.getDatabase();
176
+ await new Promise((resolve, reject) => {
177
+ const tx = db.transaction(store, "readwrite");
178
+ const request = tx.objectStore(store).delete(key);
179
+ request.onsuccess = () => resolve();
180
+ request.onerror = () => reject(request.error);
181
+ });
182
+ this.broadcast({
183
+ type: "delete",
184
+ store: String(store),
185
+ key
186
+ });
187
+ }
188
+ async clear(store) {
189
+ this.getStoreMap(store).clear();
190
+ const db = this.getDatabase();
191
+ await new Promise((resolve, reject) => {
192
+ const tx = db.transaction(store, "readwrite");
193
+ const request = tx.objectStore(store).clear();
194
+ request.onsuccess = () => resolve();
195
+ request.onerror = () => reject(request.error);
196
+ });
197
+ this.broadcast({
198
+ type: "clear",
199
+ store: String(store)
200
+ });
201
+ }
202
+ async keys(store) {
203
+ await this.hydrateStore(store);
204
+ return Array.from(this.getStoreMap(store).keys());
205
+ }
206
+ async entries(store) {
207
+ await this.hydrateStore(store);
208
+ return Array.from(this.getStoreMap(store).entries());
209
+ }
210
+ destroy() {
211
+ this.channel?.close();
212
+ this.channel = null;
213
+ }
214
+ };
215
+
216
+ // src/response.ts
217
+ async function getResponseData(response, dataType = "json") {
218
+ if (dataType !== "flexible") {
219
+ let data;
220
+ if (dataType === "json") data = await response.json();
221
+ else if (dataType === "text") data = await response.text();
222
+ else if (dataType === "blob") data = await response.blob();
223
+ else if (dataType === "bytes") {
224
+ const buffer = await response.arrayBuffer();
225
+ data = new Uint8Array(buffer);
226
+ }
227
+ return { data, dataType };
228
+ }
229
+ try {
230
+ const data = await response.json();
231
+ return { data, dataType: "json" };
232
+ } catch {
233
+ try {
234
+ const data = await response.text();
235
+ return { data, dataType: "text" };
236
+ } catch {
237
+ try {
238
+ const data = await response.blob();
239
+ return { data, dataType: "blob" };
240
+ } catch {
241
+ const buffer = await response.arrayBuffer();
242
+ const data = new Uint8Array(buffer);
243
+ return { data, dataType: "bytes" };
244
+ }
245
+ }
246
+ }
247
+ }
248
+
249
+ // src/gania.ts
250
+ var Gania = class {
251
+ online;
252
+ baseUrl;
253
+ /**
254
+ * The *`gdb`* is the indexedDB abstracted storage for the Gania data
255
+ */
256
+ gdb;
257
+ constructor(options) {
258
+ this.online = navigator.onLine;
259
+ this.gdb = new GaniaCache("ganiadb");
260
+ this.baseUrl = options?.baseUrl;
261
+ }
262
+ async request({
263
+ method,
264
+ input,
265
+ ...init
266
+ }) {
267
+ if (!this.online) {
268
+ if (method === "GET") {
269
+ const cached = await this.gdb.get(
270
+ "responses",
271
+ this.createCacheKey(method, input)
272
+ );
273
+ return {
274
+ data: cached?.data
275
+ };
276
+ }
277
+ await this.gdb.set("mutations", this.createCacheKey(method, input), {
278
+ method,
279
+ url: String(input),
280
+ body: init.body,
281
+ timestamp: Date.now()
282
+ });
283
+ return {
284
+ queued: true
285
+ };
286
+ }
287
+ if (typeof input === "string") input = `${this.baseUrl || ""}${input}`;
288
+ else input;
289
+ const response = await fetch(input, {
290
+ ...init,
291
+ method
292
+ });
293
+ const { data, dataType } = await getResponseData(
294
+ response,
295
+ init.dataType ?? "flexible"
296
+ );
297
+ if (method === "GET") {
298
+ await this.gdb.set("responses", this.createCacheKey(method, input), {
299
+ data,
300
+ timestamp: Date.now()
301
+ });
302
+ }
303
+ return {
304
+ data,
305
+ status: response.status,
306
+ statusText: response.statusText,
307
+ headers: response.headers,
308
+ ok: response.ok,
309
+ dataType
310
+ };
311
+ }
312
+ createCacheKey(method, input) {
313
+ return `${method.trim().toUpperCase()}:${String(input)}`;
314
+ }
315
+ async get(input, init) {
316
+ return this.request({ method: "GET", input, ...init });
317
+ }
318
+ async post(input, body, init) {
319
+ return this.request({
320
+ input,
321
+ method: "POST",
322
+ body,
323
+ ...init
324
+ });
325
+ }
326
+ async put(input, body, init) {
327
+ return this.request({
328
+ input,
329
+ method: "PUT",
330
+ body,
331
+ ...init
332
+ });
333
+ }
334
+ async patch(input, body, init) {
335
+ return this.request({
336
+ input,
337
+ method: "PATCH",
338
+ body,
339
+ ...init
340
+ });
341
+ }
342
+ async delete(input, init) {
343
+ return this.request({
344
+ input,
345
+ method: "DELETE",
346
+ ...init
347
+ });
348
+ }
349
+ };
350
+
351
+ // src/create.ts
352
+ function create(options) {
353
+ const context = new Gania(options);
354
+ const instance = Object.assign(
355
+ (config) => context.request(config),
356
+ {
357
+ request: context.request.bind(context),
358
+ get: context.get.bind(context),
359
+ post: context.post.bind(context),
360
+ put: context.put.bind(context),
361
+ patch: context.patch.bind(context),
362
+ delete: context.delete.bind(context),
363
+ create
364
+ }
365
+ );
366
+ return instance;
367
+ }
368
+
369
+ // src/index.ts
370
+ var gania = create();
371
+ var index_default = gania;
372
+ // Annotate the CommonJS export names for ESM import in node:
373
+ 0 && (module.exports = {
374
+ Gania,
375
+ create
376
+ });
377
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/cache/gania-cache.ts","../src/response.ts","../src/gania.ts","../src/create.ts"],"sourcesContent":["import { create } from \"./create\";\r\n\r\nconst gania = create();\r\n\r\nexport default gania\r\n\r\nexport { default as Gania } from \"./gania\";\r\nexport { create };\r\nexport * from \"./types\"\r\n","type CacheEvent =\r\n | {\r\n type: \"set\";\r\n store: string;\r\n key: IDBValidKey;\r\n value: unknown;\r\n }\r\n | {\r\n type: \"delete\";\r\n store: string;\r\n key: IDBValidKey;\r\n }\r\n | {\r\n type: \"clear\";\r\n store: string;\r\n };\r\n\r\ntype StoreDef<K = unknown, V = unknown> = {\r\n key: K;\r\n value: V;\r\n};\r\nexport type CacheSchema<T> = { [K in keyof T]: StoreDef };\r\n\r\nexport class GaniaCache<Schema extends CacheSchema<Schema>> {\r\n private db: IDBDatabase | null = null;\r\n\r\n private readonly ramCache = new Map<keyof Schema, Map<unknown, unknown>>();\r\n\r\n private readonly hydratedStores = new Set<keyof Schema>();\r\n\r\n private readonly activeHydrations = new Map<keyof Schema, Promise<void>>();\r\n\r\n private channel: BroadcastChannel | null = null;\r\n\r\n constructor(\r\n private readonly dbName: string,\r\n private readonly version = 1,\r\n ) {}\r\n\r\n private getDatabase(): IDBDatabase {\r\n if (!this.db) {\r\n throw new Error(\"GaniaCache has not been initialized.\");\r\n }\r\n\r\n return this.db;\r\n }\r\n\r\n private getStoreMap<K extends keyof Schema>(\r\n store: K,\r\n ): Map<Schema[K][\"key\"], Schema[K][\"value\"]> {\r\n const map = this.ramCache.get(store);\r\n\r\n if (!map) {\r\n throw new Error(`Store \"${String(store)}\" is not initialized.`);\r\n }\r\n\r\n return map as Map<Schema[K][\"key\"], Schema[K][\"value\"]>;\r\n }\r\n\r\n async init(storeNames: (keyof Schema & string)[]): Promise<this> {\r\n return new Promise((resolve, reject) => {\r\n const request = indexedDB.open(this.dbName, this.version);\r\n\r\n request.onupgradeneeded = () => {\r\n const db = request.result;\r\n\r\n for (const storeName of storeNames) {\r\n if (!db.objectStoreNames.contains(storeName)) {\r\n db.createObjectStore(storeName);\r\n }\r\n }\r\n };\r\n\r\n request.onsuccess = () => {\r\n this.db = request.result;\r\n\r\n for (const storeName of storeNames) {\r\n this.ramCache.set(storeName, new Map());\r\n }\r\n\r\n this.channel = new BroadcastChannel(`gania-cache:${this.dbName}`);\r\n\r\n this.channel.onmessage = (event) => this.handleBroadcast(event.data);\r\n\r\n resolve(this);\r\n };\r\n\r\n request.onerror = () => reject(request.error);\r\n });\r\n }\r\n\r\n private handleBroadcast(event: CacheEvent) {\r\n const storeMap = this.ramCache.get(event.store as keyof Schema);\r\n\r\n if (!storeMap) return;\r\n\r\n switch (event.type) {\r\n case \"set\":\r\n storeMap.set(event.key, event.value);\r\n break;\r\n\r\n case \"delete\":\r\n storeMap.delete(event.key);\r\n break;\r\n\r\n case \"clear\":\r\n storeMap.clear();\r\n break;\r\n }\r\n }\r\n\r\n private broadcast(event: CacheEvent) {\r\n this.channel?.postMessage(event);\r\n }\r\n private async performHydration<K extends keyof Schema>(\r\n store: K,\r\n ): Promise<void> {\r\n const db = this.getDatabase();\r\n\r\n const tx = db.transaction(store as string, \"readonly\");\r\n\r\n const objectStore = tx.objectStore(store as string);\r\n\r\n const [keys, values] = await Promise.all([\r\n new Promise<IDBValidKey[]>((resolve, reject) => {\r\n const req = objectStore.getAllKeys();\r\n\r\n req.onsuccess = () => resolve(req.result as IDBValidKey[]);\r\n\r\n req.onerror = () => reject(req.error);\r\n }),\r\n\r\n new Promise<unknown[]>((resolve, reject) => {\r\n const req = objectStore.getAll();\r\n\r\n req.onsuccess = () => resolve(req.result);\r\n\r\n req.onerror = () => reject(req.error);\r\n }),\r\n ]);\r\n\r\n const storeMap = this.getStoreMap(store);\r\n\r\n keys.forEach((key, index) => {\r\n storeMap.set(\r\n key as Schema[K][\"key\"],\r\n values[index] as Schema[K][\"value\"],\r\n );\r\n });\r\n\r\n this.hydratedStores.add(store);\r\n }\r\n private async hydrateStore<K extends keyof Schema>(store: K): Promise<void> {\r\n // Already hydrated\r\n if (this.hydratedStores.has(store)) {\r\n return;\r\n }\r\n\r\n // Hydration currently in progress\r\n const activeHydration = this.activeHydrations.get(store);\r\n\r\n if (activeHydration) {\r\n return activeHydration;\r\n }\r\n\r\n const hydrationPromise = this.performHydration(store);\r\n\r\n this.activeHydrations.set(store, hydrationPromise);\r\n\r\n try {\r\n await hydrationPromise;\r\n } finally {\r\n this.activeHydrations.delete(store);\r\n }\r\n }\r\n\r\n async get<K extends keyof Schema>(\r\n store: K,\r\n key: Schema[K][\"key\"],\r\n ): Promise<Schema[K][\"value\"] | null> {\r\n const storeMap = this.getStoreMap(store);\r\n\r\n if (storeMap.has(key)) {\r\n return storeMap.get(key) ?? null;\r\n }\r\n\r\n const db = this.getDatabase();\r\n\r\n return new Promise((resolve) => {\r\n const tx = db.transaction(store as string, \"readonly\");\r\n\r\n const request = tx.objectStore(store as string).get(key as IDBValidKey);\r\n\r\n request.onsuccess = () => {\r\n const value = request.result ?? null;\r\n\r\n if (value !== null) {\r\n storeMap.set(key, value);\r\n }\r\n\r\n resolve(value as Schema[K][\"value\"] | null);\r\n };\r\n\r\n request.onerror = () => resolve(null);\r\n });\r\n }\r\n\r\n async set<K extends keyof Schema>(\r\n store: K,\r\n key: Schema[K][\"key\"],\r\n value: Schema[K][\"value\"],\r\n ): Promise<void> {\r\n const storeMap = this.getStoreMap(store);\r\n\r\n storeMap.set(key, value);\r\n\r\n const db = this.getDatabase();\r\n\r\n await new Promise<void>((resolve, reject) => {\r\n const tx = db.transaction(store as string, \"readwrite\");\r\n\r\n const request = tx\r\n .objectStore(store as string)\r\n .put(value, key as IDBValidKey);\r\n\r\n request.onsuccess = () => resolve();\r\n\r\n request.onerror = () => reject(request.error);\r\n });\r\n\r\n this.broadcast({\r\n type: \"set\",\r\n store: String(store),\r\n key: key as IDBValidKey,\r\n value,\r\n });\r\n }\r\n\r\n async delete<K extends keyof Schema>(\r\n store: K,\r\n key: Schema[K][\"key\"],\r\n ): Promise<void> {\r\n this.getStoreMap(store).delete(key);\r\n\r\n const db = this.getDatabase();\r\n\r\n await new Promise<void>((resolve, reject) => {\r\n const tx = db.transaction(store as string, \"readwrite\");\r\n\r\n const request = tx\r\n .objectStore(store as string)\r\n .delete(key as IDBValidKey);\r\n\r\n request.onsuccess = () => resolve();\r\n\r\n request.onerror = () => reject(request.error);\r\n });\r\n\r\n this.broadcast({\r\n type: \"delete\",\r\n store: String(store),\r\n key: key as IDBValidKey,\r\n });\r\n }\r\n\r\n async clear<K extends keyof Schema>(store: K): Promise<void> {\r\n this.getStoreMap(store).clear();\r\n\r\n const db = this.getDatabase();\r\n\r\n await new Promise<void>((resolve, reject) => {\r\n const tx = db.transaction(store as string, \"readwrite\");\r\n\r\n const request = tx.objectStore(store as string).clear();\r\n\r\n request.onsuccess = () => resolve();\r\n\r\n request.onerror = () => reject(request.error);\r\n });\r\n\r\n this.broadcast({\r\n type: \"clear\",\r\n store: String(store),\r\n });\r\n }\r\n\r\n async keys<K extends keyof Schema>(store: K): Promise<Schema[K][\"key\"][]> {\r\n await this.hydrateStore(store);\r\n\r\n return Array.from(this.getStoreMap(store).keys());\r\n }\r\n\r\n async entries<K extends keyof Schema>(\r\n store: K,\r\n ): Promise<[Schema[K][\"key\"], Schema[K][\"value\"]][]> {\r\n await this.hydrateStore(store);\r\n\r\n return Array.from(this.getStoreMap(store).entries());\r\n }\r\n\r\n destroy() {\r\n this.channel?.close();\r\n this.channel = null;\r\n }\r\n}\r\n","import { GaniaResponse, ResponseDataType } from \"./types\";\r\n\r\nexport class GaniaError<T = any> extends Error {\r\n response?: GaniaResponse<T>;\r\n\r\n constructor(message: string, response?: GaniaResponse<T>) {\r\n super(message);\r\n this.name = \"GaniaError\";\r\n this.response = response;\r\n }\r\n}\r\n\r\nexport async function getResponseData<T = any>(\r\n response: Response,\r\n dataType: ResponseDataType = \"json\",\r\n): Promise<{ data: T; dataType: ResponseDataType }> {\r\n // Handle explicit types safely without unsafe dynamic property lookups\r\n if (dataType !== \"flexible\") {\r\n let data: any;\r\n if (dataType === \"json\") data = await response.json();\r\n else if (dataType === \"text\") data = await response.text();\r\n else if (dataType === \"blob\") data = await response.blob();\r\n else if (dataType === \"bytes\") {\r\n const buffer = await response.arrayBuffer();\r\n data = new Uint8Array(buffer);\r\n }\r\n return { data, dataType };\r\n }\r\n\r\n // Handle the 'flexible' inferred fallback chain correctly\r\n try {\r\n const data = await response.json();\r\n return { data, dataType: \"json\" };\r\n } catch {\r\n try {\r\n const data = await response.text() as T;\r\n return { data, dataType: \"text\" };\r\n } catch {\r\n try {\r\n const data = await response.blob() as T;\r\n return { data, dataType: \"blob\" };\r\n } catch {\r\n const buffer = await response.arrayBuffer();\r\n const data = new Uint8Array(buffer) as T;\r\n return { data, dataType: \"bytes\" };\r\n }\r\n }\r\n }\r\n}\r\n","import { GaniaCache } from \"./cache/gania-cache\";\r\nimport { getResponseData } from \"./response\";\r\nimport {\r\n BaseUrl,\r\n CreateGaniaOptions,\r\n GaniaDbSchema,\r\n GaniaRequestConfig,\r\n GaniaRequestInit,\r\n GaniaResponse,\r\n} from \"./types\";\r\n\r\nexport default class Gania {\r\n readonly online: boolean;\r\n public baseUrl?: BaseUrl;\r\n /**\r\n * The *`gdb`* is the indexedDB abstracted storage for the Gania data\r\n */\r\n private readonly gdb: GaniaCache<GaniaDbSchema>;\r\n constructor(options?: CreateGaniaOptions) {\r\n this.online = navigator.onLine;\r\n this.gdb = new GaniaCache<GaniaDbSchema>(\"ganiadb\");\r\n this.baseUrl = options?.baseUrl;\r\n }\r\n async request<T = unknown>({\r\n method,\r\n input,\r\n ...init\r\n }: GaniaRequestConfig): Promise<GaniaResponse<T>> {\r\n if (!this.online) {\r\n if (method === \"GET\") {\r\n const cached = await this.gdb.get(\r\n \"responses\",\r\n this.createCacheKey(method, input),\r\n );\r\n\r\n return {\r\n data: cached?.data as T,\r\n } as GaniaResponse<T>;\r\n }\r\n\r\n await this.gdb.set(\"mutations\", this.createCacheKey(method!, input), {\r\n method: method!,\r\n url: String(input),\r\n body: init.body,\r\n timestamp: Date.now(),\r\n });\r\n\r\n return {\r\n queued: true,\r\n } as GaniaResponse<T>;\r\n }\r\n if (typeof input === \"string\") input = `${this.baseUrl || \"\"}${input}`;\r\n else input;\r\n const response = await fetch(input, {\r\n ...init,\r\n method,\r\n });\r\n const { data, dataType } = await getResponseData<T>(\r\n response,\r\n init.dataType ?? \"flexible\",\r\n );\r\n if (method === \"GET\") {\r\n await this.gdb.set(\"responses\", this.createCacheKey(method, input), {\r\n data,\r\n timestamp: Date.now(),\r\n });\r\n }\r\n return {\r\n data,\r\n status: response.status,\r\n statusText: response.statusText,\r\n headers: response.headers,\r\n ok: response.ok,\r\n dataType,\r\n };\r\n }\r\n createCacheKey(method: string, input: string | URL | Request): string {\r\n return `${method.trim().toUpperCase()}:${String(input)}`;\r\n }\r\n async get<T = any>(input: (string | Request) | URL, init?: GaniaRequestInit) {\r\n return this.request<T>({ method: \"GET\", input, ...init });\r\n }\r\n async post<T = unknown>(\r\n input: string | URL | Request,\r\n body?: BodyInit,\r\n init?: GaniaRequestInit,\r\n ) {\r\n return this.request<T>({\r\n input,\r\n method: \"POST\",\r\n body,\r\n ...init,\r\n });\r\n }\r\n async put<T = unknown>(\r\n input: string | URL | Request,\r\n body?: BodyInit,\r\n init?: GaniaRequestInit,\r\n ) {\r\n return this.request<T>({\r\n input,\r\n method: \"PUT\",\r\n body,\r\n ...init,\r\n });\r\n }\r\n async patch<T = unknown>(\r\n input: string | URL | Request,\r\n body?: BodyInit,\r\n init?: GaniaRequestInit,\r\n ) {\r\n return this.request<T>({\r\n input,\r\n method: \"PATCH\",\r\n body,\r\n ...init,\r\n });\r\n }\r\n async delete<T = unknown>(\r\n input: string | URL | Request,\r\n init?: GaniaRequestInit,\r\n ) {\r\n return this.request<T>({\r\n input,\r\n method: \"DELETE\",\r\n ...init,\r\n });\r\n }\r\n}\r\n","import Gania from \"./gania\";\r\nimport { CreateGaniaOptions, GaniaInstance, GaniaRequestConfig } from \"./types\";\r\n\r\nexport function create(options?: CreateGaniaOptions): GaniaInstance {\r\n const context = new Gania(options);\r\n\r\n const instance = Object.assign(\r\n <T = unknown>(config: GaniaRequestConfig) => context.request<T>(config),\r\n\r\n {\r\n request: context.request.bind(context),\r\n get: context.get.bind(context),\r\n post: context.post.bind(context),\r\n put: context.put.bind(context),\r\n patch: context.patch.bind(context),\r\n delete: context.delete.bind(context),\r\n create,\r\n },\r\n ) as GaniaInstance;\r\n\r\n return instance;\r\n}"],"mappings":"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","names":[]}
@@ -0,0 +1,76 @@
1
+ type RequestStrategy = "online-first" | "cache-first" | "auto";
2
+ interface GaniaRequestInit extends RequestInit {
3
+ strategy?: RequestStrategy;
4
+ dataType?: ResponseDataType;
5
+ }
6
+ interface GaniaRequestConfig extends GaniaRequestInit {
7
+ input: string | URL | Request;
8
+ init?: GaniaRequestInit;
9
+ }
10
+ interface GaniaDbSchema {
11
+ responses: {
12
+ key: string;
13
+ value: {
14
+ data: unknown;
15
+ timestamp: number;
16
+ expiresAt?: number;
17
+ };
18
+ };
19
+ mutations: {
20
+ key: string;
21
+ value: {
22
+ method: string;
23
+ url: string;
24
+ body: unknown;
25
+ timestamp: number;
26
+ };
27
+ };
28
+ }
29
+ type BaseUrl = `/${string}` | `http://${string}` | `https://${string}`;
30
+ interface CreateGaniaOptions {
31
+ baseUrl?: BaseUrl;
32
+ }
33
+
34
+ type ResponseDataType = "text" | "blob" | "bytes" | "json" | "flexible";
35
+ interface GaniaResponse<T = any> {
36
+ data: T;
37
+ dataType: ResponseDataType;
38
+ queued?: boolean;
39
+ headers: Response["headers"];
40
+ status: Response["status"];
41
+ statusText: Response["statusText"];
42
+ ok: Response["ok"];
43
+ }
44
+ interface GaniaInstance {
45
+ <T = unknown>(config: GaniaRequestConfig): Promise<GaniaResponse<T>>;
46
+ create(options?: CreateGaniaOptions): GaniaInstance;
47
+ request: <T = unknown>(config: GaniaRequestConfig) => Promise<GaniaResponse<T>>;
48
+ put: <T = unknown>(input: string | URL | Request, body?: BodyInit, init?: GaniaRequestInit) => Promise<GaniaResponse<T>>;
49
+ patch: <T = unknown>(input: string | URL | Request, body?: BodyInit, init?: GaniaRequestInit) => Promise<GaniaResponse<T>>;
50
+ delete: <T = unknown>(input: string | URL | Request, init?: GaniaRequestInit) => Promise<GaniaResponse<T>>;
51
+ get<T = unknown>(input: string | URL | Request, init?: GaniaRequestInit): Promise<GaniaResponse<T>>;
52
+ post<T = unknown>(input: string | URL | Request, body?: BodyInit, init?: GaniaRequestInit): Promise<GaniaResponse<T>>;
53
+ }
54
+
55
+ declare function create(options?: CreateGaniaOptions): GaniaInstance;
56
+
57
+ declare class Gania {
58
+ readonly online: boolean;
59
+ baseUrl?: BaseUrl;
60
+ /**
61
+ * The *`gdb`* is the indexedDB abstracted storage for the Gania data
62
+ */
63
+ private readonly gdb;
64
+ constructor(options?: CreateGaniaOptions);
65
+ request<T = unknown>({ method, input, ...init }: GaniaRequestConfig): Promise<GaniaResponse<T>>;
66
+ createCacheKey(method: string, input: string | URL | Request): string;
67
+ get<T = any>(input: (string | Request) | URL, init?: GaniaRequestInit): Promise<GaniaResponse<T>>;
68
+ post<T = unknown>(input: string | URL | Request, body?: BodyInit, init?: GaniaRequestInit): Promise<GaniaResponse<T>>;
69
+ put<T = unknown>(input: string | URL | Request, body?: BodyInit, init?: GaniaRequestInit): Promise<GaniaResponse<T>>;
70
+ patch<T = unknown>(input: string | URL | Request, body?: BodyInit, init?: GaniaRequestInit): Promise<GaniaResponse<T>>;
71
+ delete<T = unknown>(input: string | URL | Request, init?: GaniaRequestInit): Promise<GaniaResponse<T>>;
72
+ }
73
+
74
+ declare const gania: GaniaInstance;
75
+
76
+ export { type BaseUrl, type CreateGaniaOptions, Gania, type GaniaDbSchema, type GaniaInstance, type GaniaRequestConfig, type GaniaRequestInit, type GaniaResponse, type ResponseDataType, create, gania as default };
@@ -0,0 +1,76 @@
1
+ type RequestStrategy = "online-first" | "cache-first" | "auto";
2
+ interface GaniaRequestInit extends RequestInit {
3
+ strategy?: RequestStrategy;
4
+ dataType?: ResponseDataType;
5
+ }
6
+ interface GaniaRequestConfig extends GaniaRequestInit {
7
+ input: string | URL | Request;
8
+ init?: GaniaRequestInit;
9
+ }
10
+ interface GaniaDbSchema {
11
+ responses: {
12
+ key: string;
13
+ value: {
14
+ data: unknown;
15
+ timestamp: number;
16
+ expiresAt?: number;
17
+ };
18
+ };
19
+ mutations: {
20
+ key: string;
21
+ value: {
22
+ method: string;
23
+ url: string;
24
+ body: unknown;
25
+ timestamp: number;
26
+ };
27
+ };
28
+ }
29
+ type BaseUrl = `/${string}` | `http://${string}` | `https://${string}`;
30
+ interface CreateGaniaOptions {
31
+ baseUrl?: BaseUrl;
32
+ }
33
+
34
+ type ResponseDataType = "text" | "blob" | "bytes" | "json" | "flexible";
35
+ interface GaniaResponse<T = any> {
36
+ data: T;
37
+ dataType: ResponseDataType;
38
+ queued?: boolean;
39
+ headers: Response["headers"];
40
+ status: Response["status"];
41
+ statusText: Response["statusText"];
42
+ ok: Response["ok"];
43
+ }
44
+ interface GaniaInstance {
45
+ <T = unknown>(config: GaniaRequestConfig): Promise<GaniaResponse<T>>;
46
+ create(options?: CreateGaniaOptions): GaniaInstance;
47
+ request: <T = unknown>(config: GaniaRequestConfig) => Promise<GaniaResponse<T>>;
48
+ put: <T = unknown>(input: string | URL | Request, body?: BodyInit, init?: GaniaRequestInit) => Promise<GaniaResponse<T>>;
49
+ patch: <T = unknown>(input: string | URL | Request, body?: BodyInit, init?: GaniaRequestInit) => Promise<GaniaResponse<T>>;
50
+ delete: <T = unknown>(input: string | URL | Request, init?: GaniaRequestInit) => Promise<GaniaResponse<T>>;
51
+ get<T = unknown>(input: string | URL | Request, init?: GaniaRequestInit): Promise<GaniaResponse<T>>;
52
+ post<T = unknown>(input: string | URL | Request, body?: BodyInit, init?: GaniaRequestInit): Promise<GaniaResponse<T>>;
53
+ }
54
+
55
+ declare function create(options?: CreateGaniaOptions): GaniaInstance;
56
+
57
+ declare class Gania {
58
+ readonly online: boolean;
59
+ baseUrl?: BaseUrl;
60
+ /**
61
+ * The *`gdb`* is the indexedDB abstracted storage for the Gania data
62
+ */
63
+ private readonly gdb;
64
+ constructor(options?: CreateGaniaOptions);
65
+ request<T = unknown>({ method, input, ...init }: GaniaRequestConfig): Promise<GaniaResponse<T>>;
66
+ createCacheKey(method: string, input: string | URL | Request): string;
67
+ get<T = any>(input: (string | Request) | URL, init?: GaniaRequestInit): Promise<GaniaResponse<T>>;
68
+ post<T = unknown>(input: string | URL | Request, body?: BodyInit, init?: GaniaRequestInit): Promise<GaniaResponse<T>>;
69
+ put<T = unknown>(input: string | URL | Request, body?: BodyInit, init?: GaniaRequestInit): Promise<GaniaResponse<T>>;
70
+ patch<T = unknown>(input: string | URL | Request, body?: BodyInit, init?: GaniaRequestInit): Promise<GaniaResponse<T>>;
71
+ delete<T = unknown>(input: string | URL | Request, init?: GaniaRequestInit): Promise<GaniaResponse<T>>;
72
+ }
73
+
74
+ declare const gania: GaniaInstance;
75
+
76
+ export { type BaseUrl, type CreateGaniaOptions, Gania, type GaniaDbSchema, type GaniaInstance, type GaniaRequestConfig, type GaniaRequestInit, type GaniaResponse, type ResponseDataType, create, gania as default };
package/dist/index.js ADDED
@@ -0,0 +1,349 @@
1
+ // src/cache/gania-cache.ts
2
+ var GaniaCache = class {
3
+ constructor(dbName, version = 1) {
4
+ this.dbName = dbName;
5
+ this.version = version;
6
+ }
7
+ dbName;
8
+ version;
9
+ db = null;
10
+ ramCache = /* @__PURE__ */ new Map();
11
+ hydratedStores = /* @__PURE__ */ new Set();
12
+ activeHydrations = /* @__PURE__ */ new Map();
13
+ channel = null;
14
+ getDatabase() {
15
+ if (!this.db) {
16
+ throw new Error("GaniaCache has not been initialized.");
17
+ }
18
+ return this.db;
19
+ }
20
+ getStoreMap(store) {
21
+ const map = this.ramCache.get(store);
22
+ if (!map) {
23
+ throw new Error(`Store "${String(store)}" is not initialized.`);
24
+ }
25
+ return map;
26
+ }
27
+ async init(storeNames) {
28
+ return new Promise((resolve, reject) => {
29
+ const request = indexedDB.open(this.dbName, this.version);
30
+ request.onupgradeneeded = () => {
31
+ const db = request.result;
32
+ for (const storeName of storeNames) {
33
+ if (!db.objectStoreNames.contains(storeName)) {
34
+ db.createObjectStore(storeName);
35
+ }
36
+ }
37
+ };
38
+ request.onsuccess = () => {
39
+ this.db = request.result;
40
+ for (const storeName of storeNames) {
41
+ this.ramCache.set(storeName, /* @__PURE__ */ new Map());
42
+ }
43
+ this.channel = new BroadcastChannel(`gania-cache:${this.dbName}`);
44
+ this.channel.onmessage = (event) => this.handleBroadcast(event.data);
45
+ resolve(this);
46
+ };
47
+ request.onerror = () => reject(request.error);
48
+ });
49
+ }
50
+ handleBroadcast(event) {
51
+ const storeMap = this.ramCache.get(event.store);
52
+ if (!storeMap) return;
53
+ switch (event.type) {
54
+ case "set":
55
+ storeMap.set(event.key, event.value);
56
+ break;
57
+ case "delete":
58
+ storeMap.delete(event.key);
59
+ break;
60
+ case "clear":
61
+ storeMap.clear();
62
+ break;
63
+ }
64
+ }
65
+ broadcast(event) {
66
+ this.channel?.postMessage(event);
67
+ }
68
+ async performHydration(store) {
69
+ const db = this.getDatabase();
70
+ const tx = db.transaction(store, "readonly");
71
+ const objectStore = tx.objectStore(store);
72
+ const [keys, values] = await Promise.all([
73
+ new Promise((resolve, reject) => {
74
+ const req = objectStore.getAllKeys();
75
+ req.onsuccess = () => resolve(req.result);
76
+ req.onerror = () => reject(req.error);
77
+ }),
78
+ new Promise((resolve, reject) => {
79
+ const req = objectStore.getAll();
80
+ req.onsuccess = () => resolve(req.result);
81
+ req.onerror = () => reject(req.error);
82
+ })
83
+ ]);
84
+ const storeMap = this.getStoreMap(store);
85
+ keys.forEach((key, index) => {
86
+ storeMap.set(
87
+ key,
88
+ values[index]
89
+ );
90
+ });
91
+ this.hydratedStores.add(store);
92
+ }
93
+ async hydrateStore(store) {
94
+ if (this.hydratedStores.has(store)) {
95
+ return;
96
+ }
97
+ const activeHydration = this.activeHydrations.get(store);
98
+ if (activeHydration) {
99
+ return activeHydration;
100
+ }
101
+ const hydrationPromise = this.performHydration(store);
102
+ this.activeHydrations.set(store, hydrationPromise);
103
+ try {
104
+ await hydrationPromise;
105
+ } finally {
106
+ this.activeHydrations.delete(store);
107
+ }
108
+ }
109
+ async get(store, key) {
110
+ const storeMap = this.getStoreMap(store);
111
+ if (storeMap.has(key)) {
112
+ return storeMap.get(key) ?? null;
113
+ }
114
+ const db = this.getDatabase();
115
+ return new Promise((resolve) => {
116
+ const tx = db.transaction(store, "readonly");
117
+ const request = tx.objectStore(store).get(key);
118
+ request.onsuccess = () => {
119
+ const value = request.result ?? null;
120
+ if (value !== null) {
121
+ storeMap.set(key, value);
122
+ }
123
+ resolve(value);
124
+ };
125
+ request.onerror = () => resolve(null);
126
+ });
127
+ }
128
+ async set(store, key, value) {
129
+ const storeMap = this.getStoreMap(store);
130
+ storeMap.set(key, value);
131
+ const db = this.getDatabase();
132
+ await new Promise((resolve, reject) => {
133
+ const tx = db.transaction(store, "readwrite");
134
+ const request = tx.objectStore(store).put(value, key);
135
+ request.onsuccess = () => resolve();
136
+ request.onerror = () => reject(request.error);
137
+ });
138
+ this.broadcast({
139
+ type: "set",
140
+ store: String(store),
141
+ key,
142
+ value
143
+ });
144
+ }
145
+ async delete(store, key) {
146
+ this.getStoreMap(store).delete(key);
147
+ const db = this.getDatabase();
148
+ await new Promise((resolve, reject) => {
149
+ const tx = db.transaction(store, "readwrite");
150
+ const request = tx.objectStore(store).delete(key);
151
+ request.onsuccess = () => resolve();
152
+ request.onerror = () => reject(request.error);
153
+ });
154
+ this.broadcast({
155
+ type: "delete",
156
+ store: String(store),
157
+ key
158
+ });
159
+ }
160
+ async clear(store) {
161
+ this.getStoreMap(store).clear();
162
+ const db = this.getDatabase();
163
+ await new Promise((resolve, reject) => {
164
+ const tx = db.transaction(store, "readwrite");
165
+ const request = tx.objectStore(store).clear();
166
+ request.onsuccess = () => resolve();
167
+ request.onerror = () => reject(request.error);
168
+ });
169
+ this.broadcast({
170
+ type: "clear",
171
+ store: String(store)
172
+ });
173
+ }
174
+ async keys(store) {
175
+ await this.hydrateStore(store);
176
+ return Array.from(this.getStoreMap(store).keys());
177
+ }
178
+ async entries(store) {
179
+ await this.hydrateStore(store);
180
+ return Array.from(this.getStoreMap(store).entries());
181
+ }
182
+ destroy() {
183
+ this.channel?.close();
184
+ this.channel = null;
185
+ }
186
+ };
187
+
188
+ // src/response.ts
189
+ async function getResponseData(response, dataType = "json") {
190
+ if (dataType !== "flexible") {
191
+ let data;
192
+ if (dataType === "json") data = await response.json();
193
+ else if (dataType === "text") data = await response.text();
194
+ else if (dataType === "blob") data = await response.blob();
195
+ else if (dataType === "bytes") {
196
+ const buffer = await response.arrayBuffer();
197
+ data = new Uint8Array(buffer);
198
+ }
199
+ return { data, dataType };
200
+ }
201
+ try {
202
+ const data = await response.json();
203
+ return { data, dataType: "json" };
204
+ } catch {
205
+ try {
206
+ const data = await response.text();
207
+ return { data, dataType: "text" };
208
+ } catch {
209
+ try {
210
+ const data = await response.blob();
211
+ return { data, dataType: "blob" };
212
+ } catch {
213
+ const buffer = await response.arrayBuffer();
214
+ const data = new Uint8Array(buffer);
215
+ return { data, dataType: "bytes" };
216
+ }
217
+ }
218
+ }
219
+ }
220
+
221
+ // src/gania.ts
222
+ var Gania = class {
223
+ online;
224
+ baseUrl;
225
+ /**
226
+ * The *`gdb`* is the indexedDB abstracted storage for the Gania data
227
+ */
228
+ gdb;
229
+ constructor(options) {
230
+ this.online = navigator.onLine;
231
+ this.gdb = new GaniaCache("ganiadb");
232
+ this.baseUrl = options?.baseUrl;
233
+ }
234
+ async request({
235
+ method,
236
+ input,
237
+ ...init
238
+ }) {
239
+ if (!this.online) {
240
+ if (method === "GET") {
241
+ const cached = await this.gdb.get(
242
+ "responses",
243
+ this.createCacheKey(method, input)
244
+ );
245
+ return {
246
+ data: cached?.data
247
+ };
248
+ }
249
+ await this.gdb.set("mutations", this.createCacheKey(method, input), {
250
+ method,
251
+ url: String(input),
252
+ body: init.body,
253
+ timestamp: Date.now()
254
+ });
255
+ return {
256
+ queued: true
257
+ };
258
+ }
259
+ if (typeof input === "string") input = `${this.baseUrl || ""}${input}`;
260
+ else input;
261
+ const response = await fetch(input, {
262
+ ...init,
263
+ method
264
+ });
265
+ const { data, dataType } = await getResponseData(
266
+ response,
267
+ init.dataType ?? "flexible"
268
+ );
269
+ if (method === "GET") {
270
+ await this.gdb.set("responses", this.createCacheKey(method, input), {
271
+ data,
272
+ timestamp: Date.now()
273
+ });
274
+ }
275
+ return {
276
+ data,
277
+ status: response.status,
278
+ statusText: response.statusText,
279
+ headers: response.headers,
280
+ ok: response.ok,
281
+ dataType
282
+ };
283
+ }
284
+ createCacheKey(method, input) {
285
+ return `${method.trim().toUpperCase()}:${String(input)}`;
286
+ }
287
+ async get(input, init) {
288
+ return this.request({ method: "GET", input, ...init });
289
+ }
290
+ async post(input, body, init) {
291
+ return this.request({
292
+ input,
293
+ method: "POST",
294
+ body,
295
+ ...init
296
+ });
297
+ }
298
+ async put(input, body, init) {
299
+ return this.request({
300
+ input,
301
+ method: "PUT",
302
+ body,
303
+ ...init
304
+ });
305
+ }
306
+ async patch(input, body, init) {
307
+ return this.request({
308
+ input,
309
+ method: "PATCH",
310
+ body,
311
+ ...init
312
+ });
313
+ }
314
+ async delete(input, init) {
315
+ return this.request({
316
+ input,
317
+ method: "DELETE",
318
+ ...init
319
+ });
320
+ }
321
+ };
322
+
323
+ // src/create.ts
324
+ function create(options) {
325
+ const context = new Gania(options);
326
+ const instance = Object.assign(
327
+ (config) => context.request(config),
328
+ {
329
+ request: context.request.bind(context),
330
+ get: context.get.bind(context),
331
+ post: context.post.bind(context),
332
+ put: context.put.bind(context),
333
+ patch: context.patch.bind(context),
334
+ delete: context.delete.bind(context),
335
+ create
336
+ }
337
+ );
338
+ return instance;
339
+ }
340
+
341
+ // src/index.ts
342
+ var gania = create();
343
+ var index_default = gania;
344
+ export {
345
+ Gania,
346
+ create,
347
+ index_default as default
348
+ };
349
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cache/gania-cache.ts","../src/response.ts","../src/gania.ts","../src/create.ts","../src/index.ts"],"sourcesContent":["type CacheEvent =\r\n | {\r\n type: \"set\";\r\n store: string;\r\n key: IDBValidKey;\r\n value: unknown;\r\n }\r\n | {\r\n type: \"delete\";\r\n store: string;\r\n key: IDBValidKey;\r\n }\r\n | {\r\n type: \"clear\";\r\n store: string;\r\n };\r\n\r\ntype StoreDef<K = unknown, V = unknown> = {\r\n key: K;\r\n value: V;\r\n};\r\nexport type CacheSchema<T> = { [K in keyof T]: StoreDef };\r\n\r\nexport class GaniaCache<Schema extends CacheSchema<Schema>> {\r\n private db: IDBDatabase | null = null;\r\n\r\n private readonly ramCache = new Map<keyof Schema, Map<unknown, unknown>>();\r\n\r\n private readonly hydratedStores = new Set<keyof Schema>();\r\n\r\n private readonly activeHydrations = new Map<keyof Schema, Promise<void>>();\r\n\r\n private channel: BroadcastChannel | null = null;\r\n\r\n constructor(\r\n private readonly dbName: string,\r\n private readonly version = 1,\r\n ) {}\r\n\r\n private getDatabase(): IDBDatabase {\r\n if (!this.db) {\r\n throw new Error(\"GaniaCache has not been initialized.\");\r\n }\r\n\r\n return this.db;\r\n }\r\n\r\n private getStoreMap<K extends keyof Schema>(\r\n store: K,\r\n ): Map<Schema[K][\"key\"], Schema[K][\"value\"]> {\r\n const map = this.ramCache.get(store);\r\n\r\n if (!map) {\r\n throw new Error(`Store \"${String(store)}\" is not initialized.`);\r\n }\r\n\r\n return map as Map<Schema[K][\"key\"], Schema[K][\"value\"]>;\r\n }\r\n\r\n async init(storeNames: (keyof Schema & string)[]): Promise<this> {\r\n return new Promise((resolve, reject) => {\r\n const request = indexedDB.open(this.dbName, this.version);\r\n\r\n request.onupgradeneeded = () => {\r\n const db = request.result;\r\n\r\n for (const storeName of storeNames) {\r\n if (!db.objectStoreNames.contains(storeName)) {\r\n db.createObjectStore(storeName);\r\n }\r\n }\r\n };\r\n\r\n request.onsuccess = () => {\r\n this.db = request.result;\r\n\r\n for (const storeName of storeNames) {\r\n this.ramCache.set(storeName, new Map());\r\n }\r\n\r\n this.channel = new BroadcastChannel(`gania-cache:${this.dbName}`);\r\n\r\n this.channel.onmessage = (event) => this.handleBroadcast(event.data);\r\n\r\n resolve(this);\r\n };\r\n\r\n request.onerror = () => reject(request.error);\r\n });\r\n }\r\n\r\n private handleBroadcast(event: CacheEvent) {\r\n const storeMap = this.ramCache.get(event.store as keyof Schema);\r\n\r\n if (!storeMap) return;\r\n\r\n switch (event.type) {\r\n case \"set\":\r\n storeMap.set(event.key, event.value);\r\n break;\r\n\r\n case \"delete\":\r\n storeMap.delete(event.key);\r\n break;\r\n\r\n case \"clear\":\r\n storeMap.clear();\r\n break;\r\n }\r\n }\r\n\r\n private broadcast(event: CacheEvent) {\r\n this.channel?.postMessage(event);\r\n }\r\n private async performHydration<K extends keyof Schema>(\r\n store: K,\r\n ): Promise<void> {\r\n const db = this.getDatabase();\r\n\r\n const tx = db.transaction(store as string, \"readonly\");\r\n\r\n const objectStore = tx.objectStore(store as string);\r\n\r\n const [keys, values] = await Promise.all([\r\n new Promise<IDBValidKey[]>((resolve, reject) => {\r\n const req = objectStore.getAllKeys();\r\n\r\n req.onsuccess = () => resolve(req.result as IDBValidKey[]);\r\n\r\n req.onerror = () => reject(req.error);\r\n }),\r\n\r\n new Promise<unknown[]>((resolve, reject) => {\r\n const req = objectStore.getAll();\r\n\r\n req.onsuccess = () => resolve(req.result);\r\n\r\n req.onerror = () => reject(req.error);\r\n }),\r\n ]);\r\n\r\n const storeMap = this.getStoreMap(store);\r\n\r\n keys.forEach((key, index) => {\r\n storeMap.set(\r\n key as Schema[K][\"key\"],\r\n values[index] as Schema[K][\"value\"],\r\n );\r\n });\r\n\r\n this.hydratedStores.add(store);\r\n }\r\n private async hydrateStore<K extends keyof Schema>(store: K): Promise<void> {\r\n // Already hydrated\r\n if (this.hydratedStores.has(store)) {\r\n return;\r\n }\r\n\r\n // Hydration currently in progress\r\n const activeHydration = this.activeHydrations.get(store);\r\n\r\n if (activeHydration) {\r\n return activeHydration;\r\n }\r\n\r\n const hydrationPromise = this.performHydration(store);\r\n\r\n this.activeHydrations.set(store, hydrationPromise);\r\n\r\n try {\r\n await hydrationPromise;\r\n } finally {\r\n this.activeHydrations.delete(store);\r\n }\r\n }\r\n\r\n async get<K extends keyof Schema>(\r\n store: K,\r\n key: Schema[K][\"key\"],\r\n ): Promise<Schema[K][\"value\"] | null> {\r\n const storeMap = this.getStoreMap(store);\r\n\r\n if (storeMap.has(key)) {\r\n return storeMap.get(key) ?? null;\r\n }\r\n\r\n const db = this.getDatabase();\r\n\r\n return new Promise((resolve) => {\r\n const tx = db.transaction(store as string, \"readonly\");\r\n\r\n const request = tx.objectStore(store as string).get(key as IDBValidKey);\r\n\r\n request.onsuccess = () => {\r\n const value = request.result ?? null;\r\n\r\n if (value !== null) {\r\n storeMap.set(key, value);\r\n }\r\n\r\n resolve(value as Schema[K][\"value\"] | null);\r\n };\r\n\r\n request.onerror = () => resolve(null);\r\n });\r\n }\r\n\r\n async set<K extends keyof Schema>(\r\n store: K,\r\n key: Schema[K][\"key\"],\r\n value: Schema[K][\"value\"],\r\n ): Promise<void> {\r\n const storeMap = this.getStoreMap(store);\r\n\r\n storeMap.set(key, value);\r\n\r\n const db = this.getDatabase();\r\n\r\n await new Promise<void>((resolve, reject) => {\r\n const tx = db.transaction(store as string, \"readwrite\");\r\n\r\n const request = tx\r\n .objectStore(store as string)\r\n .put(value, key as IDBValidKey);\r\n\r\n request.onsuccess = () => resolve();\r\n\r\n request.onerror = () => reject(request.error);\r\n });\r\n\r\n this.broadcast({\r\n type: \"set\",\r\n store: String(store),\r\n key: key as IDBValidKey,\r\n value,\r\n });\r\n }\r\n\r\n async delete<K extends keyof Schema>(\r\n store: K,\r\n key: Schema[K][\"key\"],\r\n ): Promise<void> {\r\n this.getStoreMap(store).delete(key);\r\n\r\n const db = this.getDatabase();\r\n\r\n await new Promise<void>((resolve, reject) => {\r\n const tx = db.transaction(store as string, \"readwrite\");\r\n\r\n const request = tx\r\n .objectStore(store as string)\r\n .delete(key as IDBValidKey);\r\n\r\n request.onsuccess = () => resolve();\r\n\r\n request.onerror = () => reject(request.error);\r\n });\r\n\r\n this.broadcast({\r\n type: \"delete\",\r\n store: String(store),\r\n key: key as IDBValidKey,\r\n });\r\n }\r\n\r\n async clear<K extends keyof Schema>(store: K): Promise<void> {\r\n this.getStoreMap(store).clear();\r\n\r\n const db = this.getDatabase();\r\n\r\n await new Promise<void>((resolve, reject) => {\r\n const tx = db.transaction(store as string, \"readwrite\");\r\n\r\n const request = tx.objectStore(store as string).clear();\r\n\r\n request.onsuccess = () => resolve();\r\n\r\n request.onerror = () => reject(request.error);\r\n });\r\n\r\n this.broadcast({\r\n type: \"clear\",\r\n store: String(store),\r\n });\r\n }\r\n\r\n async keys<K extends keyof Schema>(store: K): Promise<Schema[K][\"key\"][]> {\r\n await this.hydrateStore(store);\r\n\r\n return Array.from(this.getStoreMap(store).keys());\r\n }\r\n\r\n async entries<K extends keyof Schema>(\r\n store: K,\r\n ): Promise<[Schema[K][\"key\"], Schema[K][\"value\"]][]> {\r\n await this.hydrateStore(store);\r\n\r\n return Array.from(this.getStoreMap(store).entries());\r\n }\r\n\r\n destroy() {\r\n this.channel?.close();\r\n this.channel = null;\r\n }\r\n}\r\n","import { GaniaResponse, ResponseDataType } from \"./types\";\r\n\r\nexport class GaniaError<T = any> extends Error {\r\n response?: GaniaResponse<T>;\r\n\r\n constructor(message: string, response?: GaniaResponse<T>) {\r\n super(message);\r\n this.name = \"GaniaError\";\r\n this.response = response;\r\n }\r\n}\r\n\r\nexport async function getResponseData<T = any>(\r\n response: Response,\r\n dataType: ResponseDataType = \"json\",\r\n): Promise<{ data: T; dataType: ResponseDataType }> {\r\n // Handle explicit types safely without unsafe dynamic property lookups\r\n if (dataType !== \"flexible\") {\r\n let data: any;\r\n if (dataType === \"json\") data = await response.json();\r\n else if (dataType === \"text\") data = await response.text();\r\n else if (dataType === \"blob\") data = await response.blob();\r\n else if (dataType === \"bytes\") {\r\n const buffer = await response.arrayBuffer();\r\n data = new Uint8Array(buffer);\r\n }\r\n return { data, dataType };\r\n }\r\n\r\n // Handle the 'flexible' inferred fallback chain correctly\r\n try {\r\n const data = await response.json();\r\n return { data, dataType: \"json\" };\r\n } catch {\r\n try {\r\n const data = await response.text() as T;\r\n return { data, dataType: \"text\" };\r\n } catch {\r\n try {\r\n const data = await response.blob() as T;\r\n return { data, dataType: \"blob\" };\r\n } catch {\r\n const buffer = await response.arrayBuffer();\r\n const data = new Uint8Array(buffer) as T;\r\n return { data, dataType: \"bytes\" };\r\n }\r\n }\r\n }\r\n}\r\n","import { GaniaCache } from \"./cache/gania-cache\";\r\nimport { getResponseData } from \"./response\";\r\nimport {\r\n BaseUrl,\r\n CreateGaniaOptions,\r\n GaniaDbSchema,\r\n GaniaRequestConfig,\r\n GaniaRequestInit,\r\n GaniaResponse,\r\n} from \"./types\";\r\n\r\nexport default class Gania {\r\n readonly online: boolean;\r\n public baseUrl?: BaseUrl;\r\n /**\r\n * The *`gdb`* is the indexedDB abstracted storage for the Gania data\r\n */\r\n private readonly gdb: GaniaCache<GaniaDbSchema>;\r\n constructor(options?: CreateGaniaOptions) {\r\n this.online = navigator.onLine;\r\n this.gdb = new GaniaCache<GaniaDbSchema>(\"ganiadb\");\r\n this.baseUrl = options?.baseUrl;\r\n }\r\n async request<T = unknown>({\r\n method,\r\n input,\r\n ...init\r\n }: GaniaRequestConfig): Promise<GaniaResponse<T>> {\r\n if (!this.online) {\r\n if (method === \"GET\") {\r\n const cached = await this.gdb.get(\r\n \"responses\",\r\n this.createCacheKey(method, input),\r\n );\r\n\r\n return {\r\n data: cached?.data as T,\r\n } as GaniaResponse<T>;\r\n }\r\n\r\n await this.gdb.set(\"mutations\", this.createCacheKey(method!, input), {\r\n method: method!,\r\n url: String(input),\r\n body: init.body,\r\n timestamp: Date.now(),\r\n });\r\n\r\n return {\r\n queued: true,\r\n } as GaniaResponse<T>;\r\n }\r\n if (typeof input === \"string\") input = `${this.baseUrl || \"\"}${input}`;\r\n else input;\r\n const response = await fetch(input, {\r\n ...init,\r\n method,\r\n });\r\n const { data, dataType } = await getResponseData<T>(\r\n response,\r\n init.dataType ?? \"flexible\",\r\n );\r\n if (method === \"GET\") {\r\n await this.gdb.set(\"responses\", this.createCacheKey(method, input), {\r\n data,\r\n timestamp: Date.now(),\r\n });\r\n }\r\n return {\r\n data,\r\n status: response.status,\r\n statusText: response.statusText,\r\n headers: response.headers,\r\n ok: response.ok,\r\n dataType,\r\n };\r\n }\r\n createCacheKey(method: string, input: string | URL | Request): string {\r\n return `${method.trim().toUpperCase()}:${String(input)}`;\r\n }\r\n async get<T = any>(input: (string | Request) | URL, init?: GaniaRequestInit) {\r\n return this.request<T>({ method: \"GET\", input, ...init });\r\n }\r\n async post<T = unknown>(\r\n input: string | URL | Request,\r\n body?: BodyInit,\r\n init?: GaniaRequestInit,\r\n ) {\r\n return this.request<T>({\r\n input,\r\n method: \"POST\",\r\n body,\r\n ...init,\r\n });\r\n }\r\n async put<T = unknown>(\r\n input: string | URL | Request,\r\n body?: BodyInit,\r\n init?: GaniaRequestInit,\r\n ) {\r\n return this.request<T>({\r\n input,\r\n method: \"PUT\",\r\n body,\r\n ...init,\r\n });\r\n }\r\n async patch<T = unknown>(\r\n input: string | URL | Request,\r\n body?: BodyInit,\r\n init?: GaniaRequestInit,\r\n ) {\r\n return this.request<T>({\r\n input,\r\n method: \"PATCH\",\r\n body,\r\n ...init,\r\n });\r\n }\r\n async delete<T = unknown>(\r\n input: string | URL | Request,\r\n init?: GaniaRequestInit,\r\n ) {\r\n return this.request<T>({\r\n input,\r\n method: \"DELETE\",\r\n ...init,\r\n });\r\n }\r\n}\r\n","import Gania from \"./gania\";\r\nimport { CreateGaniaOptions, GaniaInstance, GaniaRequestConfig } from \"./types\";\r\n\r\nexport function create(options?: CreateGaniaOptions): GaniaInstance {\r\n const context = new Gania(options);\r\n\r\n const instance = Object.assign(\r\n <T = unknown>(config: GaniaRequestConfig) => context.request<T>(config),\r\n\r\n {\r\n request: context.request.bind(context),\r\n get: context.get.bind(context),\r\n post: context.post.bind(context),\r\n put: context.put.bind(context),\r\n patch: context.patch.bind(context),\r\n delete: context.delete.bind(context),\r\n create,\r\n },\r\n ) as GaniaInstance;\r\n\r\n return instance;\r\n}","import { create } from \"./create\";\r\n\r\nconst gania = create();\r\n\r\nexport default gania\r\n\r\nexport { default as Gania } from \"./gania\";\r\nexport { create };\r\nexport * from \"./types\"\r\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@nditure/gania",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js",
15
+ "require": "./dist/index.cjs"
16
+ }
17
+ },
18
+ "keywords": [],
19
+ "author": "",
20
+ "license": "ISC",
21
+ "scripts": {
22
+ "build": "tsup"
23
+ }
24
+ }