@sascha384/tic 1.14.0 → 1.16.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.
Files changed (52) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dist/app.d.ts +2 -0
  3. package/dist/app.js +14 -1
  4. package/dist/app.js.map +1 -1
  5. package/dist/backends/availability.d.ts +14 -0
  6. package/dist/backends/availability.js +41 -0
  7. package/dist/backends/availability.js.map +1 -0
  8. package/dist/backends/factory.js +9 -5
  9. package/dist/backends/factory.js.map +1 -1
  10. package/dist/backends/jira/config.js +5 -1
  11. package/dist/backends/jira/config.js.map +1 -1
  12. package/dist/backends/local/config.d.ts +2 -0
  13. package/dist/backends/local/config.js +1 -0
  14. package/dist/backends/local/config.js.map +1 -1
  15. package/dist/backends/local/index.d.ts +0 -2
  16. package/dist/backends/local/index.js +31 -31
  17. package/dist/backends/local/index.js.map +1 -1
  18. package/dist/components/CardLayout.js +2 -0
  19. package/dist/components/CardLayout.js.map +1 -1
  20. package/dist/components/DefaultPicker.d.ts +8 -0
  21. package/dist/components/DefaultPicker.js +19 -0
  22. package/dist/components/DefaultPicker.js.map +1 -0
  23. package/dist/components/Header.js +3 -4
  24. package/dist/components/Header.js.map +1 -1
  25. package/dist/components/IterationPicker.js +9 -3
  26. package/dist/components/IterationPicker.js.map +1 -1
  27. package/dist/components/Settings.js +145 -36
  28. package/dist/components/Settings.js.map +1 -1
  29. package/dist/components/StatusScreen.js +3 -8
  30. package/dist/components/StatusScreen.js.map +1 -1
  31. package/dist/components/WorkItemForm.js +10 -2
  32. package/dist/components/WorkItemForm.js.map +1 -1
  33. package/dist/components/WorkItemList.js +42 -32
  34. package/dist/components/WorkItemList.js.map +1 -1
  35. package/dist/index.js +8 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/stores/backendDataStore.d.ts +24 -0
  38. package/dist/stores/backendDataStore.js +115 -0
  39. package/dist/stores/backendDataStore.js.map +1 -0
  40. package/dist/stores/configStore.d.ts +10 -0
  41. package/dist/stores/configStore.js +80 -0
  42. package/dist/stores/configStore.js.map +1 -0
  43. package/dist/update-checker.d.ts +6 -0
  44. package/dist/update-checker.js +27 -0
  45. package/dist/update-checker.js.map +1 -0
  46. package/dist/updater.d.ts +6 -0
  47. package/dist/updater.js +37 -0
  48. package/dist/updater.js.map +1 -0
  49. package/package.json +5 -2
  50. package/dist/hooks/useBackendData.d.ts +0 -17
  51. package/dist/hooks/useBackendData.js +0 -98
  52. package/dist/hooks/useBackendData.js.map +0 -1
@@ -0,0 +1,115 @@
1
+ import { createStore } from 'zustand/vanilla';
2
+ import { useStore } from 'zustand';
3
+ const defaultCapabilities = {
4
+ relationships: false,
5
+ customTypes: false,
6
+ customStatuses: false,
7
+ iterations: false,
8
+ comments: false,
9
+ fields: {
10
+ priority: false,
11
+ assignee: false,
12
+ labels: false,
13
+ parent: false,
14
+ dependsOn: false,
15
+ },
16
+ templates: false,
17
+ templateFields: {
18
+ type: false,
19
+ status: false,
20
+ priority: false,
21
+ assignee: false,
22
+ labels: false,
23
+ iteration: false,
24
+ parent: false,
25
+ dependsOn: false,
26
+ description: false,
27
+ },
28
+ };
29
+ // Module-level references (not reactive state)
30
+ let currentBackend = null;
31
+ let currentSyncManager = null;
32
+ export const backendDataStore = createStore((set, get) => ({
33
+ items: [],
34
+ capabilities: { ...defaultCapabilities },
35
+ statuses: [],
36
+ iterations: [],
37
+ types: [],
38
+ assignees: [],
39
+ labels: [],
40
+ currentIteration: '',
41
+ loaded: false,
42
+ loading: false,
43
+ error: null,
44
+ syncStatus: null,
45
+ async init(backend, syncManager) {
46
+ get().destroy();
47
+ currentBackend = backend;
48
+ currentSyncManager = syncManager ?? null;
49
+ set({ loading: true });
50
+ if (currentSyncManager) {
51
+ currentSyncManager.onStatusChange((status) => {
52
+ get().setSyncStatus(status);
53
+ if (status.state === 'idle') {
54
+ void get().refresh();
55
+ }
56
+ });
57
+ }
58
+ await get().refresh();
59
+ set({ loaded: true, loading: false });
60
+ },
61
+ async refresh() {
62
+ if (!currentBackend)
63
+ return;
64
+ try {
65
+ const iter = await currentBackend.getCurrentIteration();
66
+ const [statuses, iterations, types, assignees, labels, items] = await Promise.all([
67
+ currentBackend.getStatuses(),
68
+ currentBackend.getIterations(),
69
+ currentBackend.getWorkItemTypes(),
70
+ currentBackend.getAssignees().catch(() => []),
71
+ currentBackend.getLabels().catch(() => []),
72
+ currentBackend.listWorkItems(iter),
73
+ ]);
74
+ set({
75
+ capabilities: currentBackend.getCapabilities(),
76
+ statuses,
77
+ iterations,
78
+ types,
79
+ assignees,
80
+ labels,
81
+ currentIteration: iter,
82
+ items,
83
+ error: null,
84
+ });
85
+ }
86
+ catch (e) {
87
+ set({ error: e instanceof Error ? e.message : String(e) });
88
+ }
89
+ },
90
+ setSyncStatus(status) {
91
+ set({ syncStatus: status });
92
+ },
93
+ destroy() {
94
+ currentBackend = null;
95
+ currentSyncManager = null;
96
+ set({
97
+ items: [],
98
+ capabilities: { ...defaultCapabilities },
99
+ statuses: [],
100
+ iterations: [],
101
+ types: [],
102
+ assignees: [],
103
+ labels: [],
104
+ currentIteration: '',
105
+ loaded: false,
106
+ loading: false,
107
+ error: null,
108
+ syncStatus: null,
109
+ });
110
+ },
111
+ }));
112
+ export function useBackendDataStore(selector) {
113
+ return useStore(backendDataStore, selector);
114
+ }
115
+ //# sourceMappingURL=backendDataStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backendDataStore.js","sourceRoot":"","sources":["../../src/stores/backendDataStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { type Config } from '../backends/local/config.js';
2
+ export interface ConfigStoreState {
3
+ config: Config;
4
+ loaded: boolean;
5
+ init(root: string): Promise<void>;
6
+ update(partial: Partial<Config>): Promise<void>;
7
+ destroy(): void;
8
+ }
9
+ export declare const configStore: import("zustand").StoreApi<ConfigStoreState>;
10
+ export declare function useConfigStore<T>(selector: (state: ConfigStoreState) => T): T;
@@ -0,0 +1,80 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createStore } from 'zustand/vanilla';
4
+ import { useStore } from 'zustand';
5
+ import { defaultConfig, readConfig, writeConfig, } from '../backends/local/config.js';
6
+ const WATCH_DEBOUNCE_MS = 50;
7
+ const WRITE_FLAG_DURATION_MS = 100;
8
+ let watcher = null;
9
+ let debounceTimer = null;
10
+ let writingTimer = null;
11
+ let writing = false;
12
+ let currentRoot = '';
13
+ export const configStore = createStore((set, get) => ({
14
+ config: { ...defaultConfig },
15
+ loaded: false,
16
+ async init(root) {
17
+ // Clean up any existing watcher from a prior init
18
+ get().destroy();
19
+ currentRoot = root;
20
+ const config = await readConfig(root);
21
+ set({ config, loaded: true });
22
+ // Ensure .tic dir exists before watching
23
+ const ticDir = path.join(root, '.tic');
24
+ fs.mkdirSync(ticDir, { recursive: true });
25
+ const configPath = path.join(ticDir, 'config.yml');
26
+ // Ensure the config file exists so fs.watch doesn't error
27
+ if (!fs.existsSync(configPath)) {
28
+ await writeConfig(root, config);
29
+ }
30
+ watcher = fs.watch(configPath, () => {
31
+ if (writing)
32
+ return;
33
+ if (debounceTimer)
34
+ clearTimeout(debounceTimer);
35
+ debounceTimer = setTimeout(() => {
36
+ debounceTimer = null;
37
+ readConfig(currentRoot).then((updated) => set({ config: updated }), () => {
38
+ // File may have been deleted or be mid-write; ignore
39
+ });
40
+ }, WATCH_DEBOUNCE_MS);
41
+ });
42
+ watcher.on('error', () => {
43
+ watcher?.close();
44
+ watcher = null;
45
+ });
46
+ },
47
+ async update(partial) {
48
+ const merged = { ...get().config, ...partial };
49
+ set({ config: merged });
50
+ writing = true;
51
+ await writeConfig(currentRoot, merged);
52
+ // Keep the writing flag on long enough for the watcher to fire and be ignored
53
+ if (writingTimer)
54
+ clearTimeout(writingTimer);
55
+ writingTimer = setTimeout(() => {
56
+ writing = false;
57
+ writingTimer = null;
58
+ }, WRITE_FLAG_DURATION_MS);
59
+ },
60
+ destroy() {
61
+ if (watcher) {
62
+ watcher.close();
63
+ watcher = null;
64
+ }
65
+ if (debounceTimer) {
66
+ clearTimeout(debounceTimer);
67
+ debounceTimer = null;
68
+ }
69
+ if (writingTimer) {
70
+ clearTimeout(writingTimer);
71
+ writingTimer = null;
72
+ }
73
+ writing = false;
74
+ set({ config: { ...defaultConfig }, loaded: false });
75
+ },
76
+ }));
77
+ export function useConfigStore(selector) {
78
+ return useStore(configStore, selector);
79
+ }
80
+ //# sourceMappingURL=configStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configStore.js","sourceRoot":"","sources":["../../src/stores/configStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export interface UpdateInfo {
2
+ current: string;
3
+ latest: string;
4
+ updateAvailable: boolean;
5
+ }
6
+ export declare function checkForUpdate(): Promise<UpdateInfo | null>;
@@ -0,0 +1,27 @@
1
+ import semver from 'semver';
2
+ import { VERSION } from './version.js';
3
+ const REGISTRY_URL = 'https://registry.npmjs.org/@sascha384/tic/latest';
4
+ const TIMEOUT_MS = 5000;
5
+ export async function checkForUpdate() {
6
+ try {
7
+ const controller = new AbortController();
8
+ const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
9
+ const response = await fetch(REGISTRY_URL, { signal: controller.signal });
10
+ clearTimeout(timeout);
11
+ if (!response.ok)
12
+ return null;
13
+ const data = (await response.json());
14
+ const latest = data['version'];
15
+ if (typeof latest !== 'string')
16
+ return null;
17
+ return {
18
+ current: VERSION,
19
+ latest,
20
+ updateAvailable: semver.gt(latest, VERSION),
21
+ };
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ //# sourceMappingURL=update-checker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-checker.js","sourceRoot":"","sources":["../src/update-checker.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAQvC,MAAM,YAAY,GAAG,kDAAkD,CAAC;AACxE,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,UAAU,CAAC,CAAC;QAEjE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,YAAY,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1E,YAAY,CAAC,OAAO,CAAC,CAAC;QAEtB,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QAE9B,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA4B,CAAC;QAChE,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;QAC/B,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE5C,OAAO;YACL,OAAO,EAAE,OAAO;YAChB,MAAM;YACN,eAAe,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;SAC5C,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare function buildUpdateCommand(): string;
2
+ export declare function buildRelaunchArgs(originalArgs: string[]): {
3
+ command: string;
4
+ args: string[];
5
+ };
6
+ export declare function runUpdate(originalArgs: string[]): void;
@@ -0,0 +1,37 @@
1
+ import { execSync, spawn } from 'node:child_process';
2
+ const PACKAGE_NAME = '@sascha384/tic';
3
+ export function buildUpdateCommand() {
4
+ return `npm install -g ${PACKAGE_NAME}@latest`;
5
+ }
6
+ export function buildRelaunchArgs(originalArgs) {
7
+ return { command: 'tic', args: originalArgs };
8
+ }
9
+ export function runUpdate(originalArgs) {
10
+ const cmd = buildUpdateCommand();
11
+ console.log(`\nUpdating ${PACKAGE_NAME}...\n`);
12
+ try {
13
+ execSync(cmd, { stdio: 'inherit' });
14
+ }
15
+ catch {
16
+ console.error(`\nUpdate failed. Run manually: ${cmd}\n`);
17
+ process.exit(1);
18
+ }
19
+ console.log('\nUpdate complete! Restarting tic...\n');
20
+ const { command, args } = buildRelaunchArgs(originalArgs);
21
+ const child = spawn(command, args, {
22
+ stdio: 'inherit',
23
+ detached: false,
24
+ });
25
+ child.on('exit', (code) => {
26
+ process.exit(code ?? 0);
27
+ });
28
+ }
29
+ // When run directly as a script
30
+ const isDirectRun = process.argv[1] &&
31
+ (process.argv[1].endsWith('/updater.js') ||
32
+ process.argv[1].endsWith('\\updater.js'));
33
+ if (isDirectRun) {
34
+ const originalArgs = process.argv.slice(2);
35
+ runUpdate(originalArgs);
36
+ }
37
+ //# sourceMappingURL=updater.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updater.js","sourceRoot":"","sources":["../src/updater.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAEtC,MAAM,UAAU,kBAAkB;IAChC,OAAO,kBAAkB,YAAY,SAAS,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,YAAsB;IAItD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,YAAsB;IAC9C,MAAM,GAAG,GAAG,kBAAkB,EAAE,CAAC;IAEjC,OAAO,CAAC,GAAG,CAAC,cAAc,YAAY,OAAO,CAAC,CAAC;IAE/C,IAAI,CAAC;QACH,QAAQ,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CAAC,kCAAkC,GAAG,IAAI,CAAC,CAAC;QACzD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;IAEtD,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,iBAAiB,CAAC,YAAY,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE;QACjC,KAAK,EAAE,SAAS;QAChB,QAAQ,EAAE,KAAK;KAChB,CAAC,CAAC;IAEH,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QACxB,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,gCAAgC;AAChC,MAAM,WAAW,GACf,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACf,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC;QACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC;AAE9C,IAAI,WAAW,EAAE,CAAC;IAChB,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3C,SAAS,CAAC,YAAY,CAAC,CAAC;AAC1B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sascha384/tic",
3
- "version": "1.14.0",
3
+ "version": "1.16.0",
4
4
  "description": "Terminal UI for issue tracking",
5
5
  "type": "module",
6
6
  "bin": {
@@ -42,9 +42,11 @@
42
42
  "ink-spinner": "^5.0.0",
43
43
  "ink-text-input": "^6.0.0",
44
44
  "react": "^19.2.4",
45
+ "semver": "^7.7.3",
45
46
  "turndown": "^7.2.2",
46
47
  "yaml": "^2.8.2",
47
- "zod": "^4.3.6"
48
+ "zod": "^4.3.6",
49
+ "zustand": "^5.0.11"
48
50
  },
49
51
  "devDependencies": {
50
52
  "@eslint/js": "^9.39.2",
@@ -53,6 +55,7 @@
53
55
  "@sindresorhus/tsconfig": "^8.1.0",
54
56
  "@types/node": "^25.1.0",
55
57
  "@types/react": "^19.2.10",
58
+ "@types/semver": "^7.7.1",
56
59
  "@types/turndown": "^5.0.6",
57
60
  "eslint": "^9.39.2",
58
61
  "eslint-config-prettier": "^10.1.8",
@@ -1,17 +0,0 @@
1
- import type { Backend, BackendCapabilities } from '../backends/types.js';
2
- import type { WorkItem } from '../types.js';
3
- export interface BackendData {
4
- capabilities: BackendCapabilities;
5
- statuses: string[];
6
- iterations: string[];
7
- types: string[];
8
- assignees: string[];
9
- labels: string[];
10
- currentIteration: string;
11
- items: WorkItem[];
12
- loading: boolean;
13
- error: string | null;
14
- refresh: () => void;
15
- }
16
- export declare function useBackendData(backend: Backend, iteration?: string): BackendData;
17
- export declare function clearBackendDataCache(): void;
@@ -1,98 +0,0 @@
1
- import { useState, useEffect, useCallback, useRef } from 'react';
2
- let dataCache = null;
3
- export function useBackendData(backend, iteration) {
4
- const capabilities = backend.getCapabilities(); // sync — no I/O
5
- // Check cache at mount time and store in ref (won't change during lifecycle)
6
- const hadCacheAtMount = useRef(dataCache !== null);
7
- // Use cached data if available, otherwise start with loading state
8
- const [loading, setLoading] = useState(!hadCacheAtMount.current);
9
- const [error, setError] = useState(null);
10
- const [statuses, setStatuses] = useState(dataCache?.statuses ?? []);
11
- const [iterations, setIterations] = useState(dataCache?.iterations ?? []);
12
- const [types, setTypes] = useState(dataCache?.types ?? []);
13
- const [assignees, setAssignees] = useState(dataCache?.assignees ?? []);
14
- const [labels, setLabels] = useState(dataCache?.labels ?? []);
15
- const [currentIteration, setCurrentIteration] = useState(dataCache?.currentIteration ?? '');
16
- const [items, setItems] = useState(dataCache?.items ?? []);
17
- const [refreshCounter, setRefreshCounter] = useState(0);
18
- // Track if this is the initial mount to avoid showing loading on return
19
- const isInitialMount = useRef(true);
20
- const refresh = useCallback(() => {
21
- setRefreshCounter((c) => c + 1);
22
- }, []);
23
- useEffect(() => {
24
- let cancelled = false;
25
- // Only show loading if:
26
- // - No cache at mount time and this is initial mount, OR
27
- // - This is an explicit refresh (not initial mount)
28
- const showLoading = !hadCacheAtMount.current || !isInitialMount.current;
29
- if (showLoading) {
30
- setLoading(true);
31
- }
32
- isInitialMount.current = false;
33
- async function load() {
34
- try {
35
- const iter = iteration ?? (await backend.getCurrentIteration());
36
- const [s, it, t, a, l, wi] = await Promise.all([
37
- backend.getStatuses(),
38
- backend.getIterations(),
39
- backend.getWorkItemTypes(),
40
- backend.getAssignees().catch(() => []),
41
- backend.getLabels().catch(() => []),
42
- backend.listWorkItems(iter),
43
- ]);
44
- if (cancelled)
45
- return;
46
- setStatuses(s);
47
- setIterations(it);
48
- setTypes(t);
49
- setAssignees(a);
50
- setLabels(l);
51
- setCurrentIteration(iter);
52
- setItems(wi);
53
- setError(null);
54
- // Update cache
55
- dataCache = {
56
- statuses: s,
57
- iterations: it,
58
- types: t,
59
- assignees: a,
60
- labels: l,
61
- currentIteration: iter,
62
- items: wi,
63
- };
64
- }
65
- catch (e) {
66
- if (cancelled)
67
- return;
68
- setError(e instanceof Error ? e.message : String(e));
69
- }
70
- finally {
71
- if (!cancelled)
72
- setLoading(false);
73
- }
74
- }
75
- void load();
76
- return () => {
77
- cancelled = true;
78
- };
79
- }, [backend, iteration, refreshCounter]);
80
- return {
81
- capabilities,
82
- statuses,
83
- iterations,
84
- types,
85
- assignees,
86
- labels,
87
- currentIteration,
88
- items,
89
- loading,
90
- error,
91
- refresh,
92
- };
93
- }
94
- // Export for testing
95
- export function clearBackendDataCache() {
96
- dataCache = null;
97
- }
98
- //# sourceMappingURL=useBackendData.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useBackendData.js","sourceRoot":"","sources":["../../src/hooks/useBackendData.ts"],"names":[],"mappings":"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"}