strata-storage 2.4.3 → 2.5.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/AI-INTEGRATION-GUIDE.md +115 -261
- package/README.md +426 -182
- package/android/AGENTS.md +34 -0
- package/android/CLAUDE.md +51 -0
- package/android/src/main/java/com/strata/storage/SQLiteStorage.java +35 -0
- package/android/src/main/java/com/stratastorage/StrataStoragePlugin.java +191 -27
- package/dist/README.md +426 -182
- package/dist/adapters/capacitor/FilesystemAdapter.d.ts.map +1 -1
- package/dist/adapters/capacitor/FilesystemAdapter.js +2 -1
- package/dist/adapters/capacitor/PreferencesAdapter.d.ts.map +1 -1
- package/dist/adapters/capacitor/PreferencesAdapter.js +2 -1
- package/dist/adapters/capacitor/SecureAdapter.d.ts.map +1 -1
- package/dist/adapters/capacitor/SecureAdapter.js +2 -1
- package/dist/adapters/capacitor/SqliteAdapter.d.ts.map +1 -1
- package/dist/adapters/capacitor/SqliteAdapter.js +2 -1
- package/dist/adapters/web/CacheAdapter.d.ts.map +1 -1
- package/dist/adapters/web/CacheAdapter.js +11 -3
- package/dist/adapters/web/CookieAdapter.d.ts +37 -1
- package/dist/adapters/web/CookieAdapter.d.ts.map +1 -1
- package/dist/adapters/web/CookieAdapter.js +89 -9
- package/dist/adapters/web/IndexedDBAdapter.d.ts.map +1 -1
- package/dist/adapters/web/IndexedDBAdapter.js +10 -2
- package/dist/adapters/web/LocalStorageAdapter.d.ts +31 -0
- package/dist/adapters/web/LocalStorageAdapter.d.ts.map +1 -1
- package/dist/adapters/web/LocalStorageAdapter.js +92 -19
- package/dist/adapters/web/MemoryAdapter.d.ts +24 -0
- package/dist/adapters/web/MemoryAdapter.d.ts.map +1 -1
- package/dist/adapters/web/MemoryAdapter.js +69 -18
- package/dist/adapters/web/SessionStorageAdapter.d.ts +24 -0
- package/dist/adapters/web/SessionStorageAdapter.d.ts.map +1 -1
- package/dist/adapters/web/SessionStorageAdapter.js +71 -9
- package/dist/adapters/web/URLAdapter.d.ts +59 -0
- package/dist/adapters/web/URLAdapter.d.ts.map +1 -0
- package/dist/adapters/web/URLAdapter.js +234 -0
- package/dist/adapters/web/index.d.ts +1 -0
- package/dist/adapters/web/index.d.ts.map +1 -1
- package/dist/adapters/web/index.js +1 -0
- package/dist/android/AGENTS.md +34 -0
- package/dist/android/CLAUDE.md +51 -0
- package/dist/android/src/main/java/com/strata/storage/SQLiteStorage.java +35 -0
- package/dist/android/src/main/java/com/stratastorage/StrataStoragePlugin.java +191 -27
- package/dist/capacitor.d.ts.map +1 -1
- package/dist/capacitor.js +2 -1
- package/dist/core/BaseAdapter.d.ts +8 -0
- package/dist/core/BaseAdapter.d.ts.map +1 -1
- package/dist/core/BaseAdapter.js +34 -14
- package/dist/core/Strata.d.ts +56 -2
- package/dist/core/Strata.d.ts.map +1 -1
- package/dist/core/Strata.js +501 -53
- package/dist/features/encryption.d.ts.map +1 -1
- package/dist/features/encryption.js +3 -2
- package/dist/features/integrity.d.ts +16 -0
- package/dist/features/integrity.d.ts.map +1 -0
- package/dist/features/integrity.js +28 -0
- package/dist/features/observer.d.ts.map +1 -1
- package/dist/features/observer.js +2 -1
- package/dist/features/query.d.ts +7 -1
- package/dist/features/query.d.ts.map +1 -1
- package/dist/features/query.js +9 -2
- package/dist/features/sync.d.ts.map +1 -1
- package/dist/features/sync.js +4 -3
- package/dist/index.d.ts +35 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +55 -30
- package/dist/integrations/angular/index.d.ts +158 -0
- package/dist/integrations/angular/index.d.ts.map +1 -0
- package/dist/integrations/angular/index.js +395 -0
- package/dist/integrations/index.d.ts +15 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +18 -0
- package/dist/integrations/react/index.d.ts +75 -0
- package/dist/integrations/react/index.d.ts.map +1 -0
- package/dist/integrations/react/index.js +191 -0
- package/dist/integrations/vue/index.d.ts +103 -0
- package/dist/integrations/vue/index.d.ts.map +1 -0
- package/dist/integrations/vue/index.js +274 -0
- package/dist/ios/AGENTS.md +33 -0
- package/dist/ios/CLAUDE.md +49 -0
- package/dist/ios/Plugin/KeychainStorage.swift +139 -50
- package/dist/ios/Plugin/SQLiteStorage.swift +40 -0
- package/dist/ios/Plugin/StrataStoragePlugin.m +23 -0
- package/dist/ios/Plugin/StrataStoragePlugin.swift +201 -52
- package/dist/package.json +21 -5
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +2 -1
- package/dist/types/index.d.ts +58 -9
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +0 -13
- package/dist/utils/errors.d.ts +7 -0
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +15 -3
- package/dist/utils/index.d.ts +63 -5
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +109 -16
- package/dist/utils/logger.d.ts +31 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +63 -0
- package/ios/AGENTS.md +33 -0
- package/ios/CLAUDE.md +49 -0
- package/ios/Plugin/KeychainStorage.swift +139 -50
- package/ios/Plugin/SQLiteStorage.swift +40 -0
- package/ios/Plugin/StrataStoragePlugin.m +23 -0
- package/ios/Plugin/StrataStoragePlugin.swift +201 -52
- package/package.json +31 -20
- package/scripts/build.js +16 -5
- package/scripts/configure.js +2 -6
- package/scripts/postinstall.js +2 -2
- package/Readme.md +0 -271
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* React integration for Strata Storage.
|
|
4
|
+
*
|
|
5
|
+
* Two usage styles, both fully supported:
|
|
6
|
+
*
|
|
7
|
+
* 1. No-provider (Zustand-style, recommended): create an instance anywhere and
|
|
8
|
+
* bind hooks to it with `createStrataHooks` — no Provider required.
|
|
9
|
+
*
|
|
10
|
+
* ```tsx
|
|
11
|
+
* import { defineStorage } from 'strata-storage';
|
|
12
|
+
* import { createStrataHooks } from 'strata-storage/react';
|
|
13
|
+
* const storage = defineStorage();
|
|
14
|
+
* export const { useStorage } = createStrataHooks(storage);
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* 2. Provider-based (classic): wrap the tree in <StrataProvider> and call the
|
|
18
|
+
* exported hooks, which read the instance from context.
|
|
19
|
+
*/
|
|
20
|
+
import { createContext, useContext, useEffect, useState, useCallback, useMemo } from 'react';
|
|
21
|
+
import { Strata } from "../../core/Strata.js";
|
|
22
|
+
import { ValidationError } from "../../utils/errors.js";
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Shared hook implementations (the Strata instance is passed in explicitly).
|
|
25
|
+
// Both the no-provider hooks and the Provider-based hooks delegate here, so the
|
|
26
|
+
// logic lives in exactly one place.
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
function useStorageImpl(strata, key, defaultValue, options) {
|
|
29
|
+
const [value, setValue] = useState(null);
|
|
30
|
+
const [loading, setLoading] = useState(true);
|
|
31
|
+
// Load the initial value.
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
let active = true;
|
|
34
|
+
strata.get(key, options).then((val) => {
|
|
35
|
+
if (!active)
|
|
36
|
+
return;
|
|
37
|
+
setValue(val ?? defaultValue ?? null);
|
|
38
|
+
setLoading(false);
|
|
39
|
+
});
|
|
40
|
+
return () => {
|
|
41
|
+
active = false;
|
|
42
|
+
};
|
|
43
|
+
}, [key, strata, options, defaultValue]);
|
|
44
|
+
// Subscribe to changes for this key.
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
return strata.subscribe((change) => {
|
|
47
|
+
if (change.key === key) {
|
|
48
|
+
setValue(change.newValue);
|
|
49
|
+
}
|
|
50
|
+
}, options);
|
|
51
|
+
}, [key, strata, options]);
|
|
52
|
+
const updateValue = useCallback(async (newValue, updateOptions) => {
|
|
53
|
+
if (newValue === null) {
|
|
54
|
+
await strata.remove(key, updateOptions ?? options);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
await strata.set(key, newValue, updateOptions ?? options);
|
|
58
|
+
}
|
|
59
|
+
setValue(newValue);
|
|
60
|
+
}, [key, strata, options]);
|
|
61
|
+
return [value, updateValue, loading];
|
|
62
|
+
}
|
|
63
|
+
function useStorageQueryImpl(strata, condition, options) {
|
|
64
|
+
const [data, setData] = useState([]);
|
|
65
|
+
const [loading, setLoading] = useState(true);
|
|
66
|
+
const conditionKey = useMemo(() => JSON.stringify(condition), [condition]);
|
|
67
|
+
const refetch = useCallback(async () => {
|
|
68
|
+
setLoading(true);
|
|
69
|
+
try {
|
|
70
|
+
setData(await strata.query(condition, options));
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
setLoading(false);
|
|
74
|
+
}
|
|
75
|
+
}, [strata, condition, options]);
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
void refetch();
|
|
78
|
+
}, [conditionKey, refetch]);
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
return strata.subscribe(() => {
|
|
81
|
+
void refetch();
|
|
82
|
+
}, options);
|
|
83
|
+
}, [strata, refetch, options]);
|
|
84
|
+
return { data, loading, refetch };
|
|
85
|
+
}
|
|
86
|
+
function useStorageTTLImpl(strata, key, options) {
|
|
87
|
+
const [ttl, setTTL] = useState(null);
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
let active = true;
|
|
90
|
+
const checkTTL = async () => {
|
|
91
|
+
const remaining = await strata.getTTL(key, options);
|
|
92
|
+
if (active)
|
|
93
|
+
setTTL(remaining);
|
|
94
|
+
};
|
|
95
|
+
void checkTTL();
|
|
96
|
+
const interval = setInterval(() => void checkTTL(), 1000);
|
|
97
|
+
return () => {
|
|
98
|
+
active = false;
|
|
99
|
+
clearInterval(interval);
|
|
100
|
+
};
|
|
101
|
+
}, [key, strata, options]);
|
|
102
|
+
const extendTTL = useCallback(async (extension) => {
|
|
103
|
+
await strata.extendTTL(key, extension, options);
|
|
104
|
+
setTTL(await strata.getTTL(key, options));
|
|
105
|
+
}, [key, strata, options]);
|
|
106
|
+
const persist = useCallback(async () => {
|
|
107
|
+
await strata.persist(key, options);
|
|
108
|
+
setTTL(null);
|
|
109
|
+
}, [key, strata, options]);
|
|
110
|
+
return { ttl, extendTTL, persist };
|
|
111
|
+
}
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// No-provider API — bind the hooks to an explicit instance (Zustand-style).
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
/**
|
|
116
|
+
* Bind the Strata hooks to a specific instance — the framework-agnostic,
|
|
117
|
+
* no-Provider pattern (mirrors Zustand's `create`). Call once at module scope
|
|
118
|
+
* and use the returned hooks in any component, no Provider needed.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```tsx
|
|
122
|
+
* const storage = defineStorage();
|
|
123
|
+
* export const { useStorage, useStorageQuery, useStorageTTL } =
|
|
124
|
+
* createStrataHooks(storage);
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export function createStrataHooks(strata) {
|
|
128
|
+
function useStorage(key, defaultValue, options) {
|
|
129
|
+
return useStorageImpl(strata, key, defaultValue, options);
|
|
130
|
+
}
|
|
131
|
+
function useStorageQuery(condition, options) {
|
|
132
|
+
return useStorageQueryImpl(strata, condition, options);
|
|
133
|
+
}
|
|
134
|
+
function useStorageTTL(key, options) {
|
|
135
|
+
return useStorageTTLImpl(strata, key, options);
|
|
136
|
+
}
|
|
137
|
+
return { useStorage, useStorageQuery, useStorageTTL };
|
|
138
|
+
}
|
|
139
|
+
const StrataContext = createContext({
|
|
140
|
+
strata: null,
|
|
141
|
+
initialized: false,
|
|
142
|
+
});
|
|
143
|
+
export function StrataProvider({ children, config, instance, loadingComponent, fallback, }) {
|
|
144
|
+
const [strata] = useState(() => instance ?? new Strata(config));
|
|
145
|
+
const [initialized, setInitialized] = useState(strata.isInitialized);
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
let active = true;
|
|
148
|
+
strata.initialize().then(() => {
|
|
149
|
+
if (active)
|
|
150
|
+
setInitialized(true);
|
|
151
|
+
});
|
|
152
|
+
return () => {
|
|
153
|
+
active = false;
|
|
154
|
+
// Only close instances this provider created — never a caller-owned one.
|
|
155
|
+
if (!instance) {
|
|
156
|
+
void strata.close();
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}, [strata, instance]);
|
|
160
|
+
const value = useMemo(() => ({
|
|
161
|
+
strata: initialized ? strata : null,
|
|
162
|
+
initialized,
|
|
163
|
+
}), [strata, initialized]);
|
|
164
|
+
if (!initialized && (loadingComponent || fallback)) {
|
|
165
|
+
return (loadingComponent ?? fallback);
|
|
166
|
+
}
|
|
167
|
+
return _jsx(StrataContext.Provider, { value: value, children: children });
|
|
168
|
+
}
|
|
169
|
+
export function useStrata() {
|
|
170
|
+
const { strata, initialized } = useContext(StrataContext);
|
|
171
|
+
if (!initialized || !strata) {
|
|
172
|
+
throw new ValidationError('useStrata must be used within <StrataProvider>. For provider-free usage, ' +
|
|
173
|
+
'create an instance with defineStorage() and bind hooks via createStrataHooks(instance).', {
|
|
174
|
+
hook: 'useStrata',
|
|
175
|
+
requiredProvider: 'StrataProvider',
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return strata;
|
|
179
|
+
}
|
|
180
|
+
export function useStrataInitialized() {
|
|
181
|
+
return useContext(StrataContext).initialized;
|
|
182
|
+
}
|
|
183
|
+
export function useStorage(key, defaultValue, options) {
|
|
184
|
+
return useStorageImpl(useStrata(), key, defaultValue, options);
|
|
185
|
+
}
|
|
186
|
+
export function useStorageQuery(condition, options) {
|
|
187
|
+
return useStorageQueryImpl(useStrata(), condition, options);
|
|
188
|
+
}
|
|
189
|
+
export function useStorageTTL(key, options) {
|
|
190
|
+
return useStorageTTLImpl(useStrata(), key, options);
|
|
191
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vue 3 integration for Strata Storage
|
|
3
|
+
*/
|
|
4
|
+
import type { Ref, ComputedRef, App } from 'vue';
|
|
5
|
+
import { Strata } from '@/core/Strata';
|
|
6
|
+
import type { StrataConfig, StorageOptions, QueryCondition } from '@/types';
|
|
7
|
+
export declare const StrataPlugin: {
|
|
8
|
+
install(app: App, config?: StrataConfig): void;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Use Strata instance
|
|
12
|
+
*/
|
|
13
|
+
export declare function useStrata(instance?: Strata): Strata;
|
|
14
|
+
/**
|
|
15
|
+
* Storage composable with reactive state
|
|
16
|
+
*/
|
|
17
|
+
export declare function useStorage<T = unknown>(key: string, defaultValue?: T, options?: StorageOptions, instance?: Strata): {
|
|
18
|
+
value: Ref<T | null>;
|
|
19
|
+
loading: Ref<boolean>;
|
|
20
|
+
error: Ref<Error | null>;
|
|
21
|
+
refresh: () => Promise<void>;
|
|
22
|
+
update: (value: T | null, options?: StorageOptions) => Promise<void>;
|
|
23
|
+
remove: () => Promise<void>;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Query composable
|
|
27
|
+
*/
|
|
28
|
+
export declare function useStorageQuery<T = unknown>(condition: QueryCondition, options?: StorageOptions, instance?: Strata): {
|
|
29
|
+
data: Ref<Array<{
|
|
30
|
+
key: string;
|
|
31
|
+
value: T;
|
|
32
|
+
}>>;
|
|
33
|
+
loading: Ref<boolean>;
|
|
34
|
+
error: Ref<Error | null>;
|
|
35
|
+
refetch: () => Promise<void>;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* TTL composable
|
|
39
|
+
*/
|
|
40
|
+
export declare function useStorageTTL(key: string, options?: StorageOptions, instance?: Strata): {
|
|
41
|
+
ttl: ComputedRef<string | null>;
|
|
42
|
+
milliseconds: Ref<number | null>;
|
|
43
|
+
extendTTL: (extension: number) => Promise<void>;
|
|
44
|
+
persist: () => Promise<void>;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Storage size composable
|
|
48
|
+
*/
|
|
49
|
+
export declare function useStorageSize(autoRefresh?: boolean, refreshInterval?: number, instance?: Strata): {
|
|
50
|
+
size: Ref<number>;
|
|
51
|
+
count: Ref<number>;
|
|
52
|
+
formatted: ComputedRef<string>;
|
|
53
|
+
refresh: () => Promise<void>;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Bind the Strata composables to a specific instance — the provider-free,
|
|
57
|
+
* no-plugin pattern. Use the returned composables in any component's setup()
|
|
58
|
+
* without installing StrataPlugin.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* import { defineStorage } from 'strata-storage';
|
|
63
|
+
* import { createStrataComposables } from 'strata-storage/vue';
|
|
64
|
+
* const storage = defineStorage();
|
|
65
|
+
* export const { useStorage } = createStrataComposables(storage);
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare function createStrataComposables(strata: Strata): {
|
|
69
|
+
useStorage: <T = unknown>(key: string, defaultValue?: T, options?: StorageOptions) => {
|
|
70
|
+
value: Ref<T | null, T | null>;
|
|
71
|
+
loading: Ref<boolean>;
|
|
72
|
+
error: Ref<Error | null>;
|
|
73
|
+
refresh: () => Promise<void>;
|
|
74
|
+
update: (value: T | null, options?: StorageOptions) => Promise<void>;
|
|
75
|
+
remove: () => Promise<void>;
|
|
76
|
+
};
|
|
77
|
+
useStorageQuery: <T = unknown>(condition: QueryCondition, options?: StorageOptions) => {
|
|
78
|
+
data: Ref<{
|
|
79
|
+
key: string;
|
|
80
|
+
value: T;
|
|
81
|
+
}[], {
|
|
82
|
+
key: string;
|
|
83
|
+
value: T;
|
|
84
|
+
}[]>;
|
|
85
|
+
loading: Ref<boolean>;
|
|
86
|
+
error: Ref<Error | null>;
|
|
87
|
+
refetch: () => Promise<void>;
|
|
88
|
+
};
|
|
89
|
+
useStorageTTL: (key: string, options?: StorageOptions) => {
|
|
90
|
+
ttl: ComputedRef<string | null>;
|
|
91
|
+
milliseconds: Ref<number | null>;
|
|
92
|
+
extendTTL: (extension: number) => Promise<void>;
|
|
93
|
+
persist: () => Promise<void>;
|
|
94
|
+
};
|
|
95
|
+
useStorageSize: (autoRefresh?: boolean, refreshInterval?: number) => {
|
|
96
|
+
size: Ref<number>;
|
|
97
|
+
count: Ref<number>;
|
|
98
|
+
formatted: ComputedRef<string>;
|
|
99
|
+
refresh: () => Promise<void>;
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
export type { Strata, StrataConfig, StorageOptions };
|
|
103
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/integrations/vue/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vue 3 integration for Strata Storage
|
|
3
|
+
*/
|
|
4
|
+
import { ref, computed, watch, onMounted, onUnmounted, inject } from 'vue';
|
|
5
|
+
import { Strata } from "../../core/Strata.js";
|
|
6
|
+
import { ValidationError } from "../../utils/errors.js";
|
|
7
|
+
import { logger } from "../../utils/logger.js";
|
|
8
|
+
// Injection key
|
|
9
|
+
const StrataKey = Symbol('strata');
|
|
10
|
+
// Plugin installation
|
|
11
|
+
export const StrataPlugin = {
|
|
12
|
+
install(app, config) {
|
|
13
|
+
const strata = new Strata(config);
|
|
14
|
+
// Initialize asynchronously
|
|
15
|
+
strata.initialize().catch(logger.error);
|
|
16
|
+
// Provide globally
|
|
17
|
+
app.provide(StrataKey, strata);
|
|
18
|
+
// Add global properties
|
|
19
|
+
app.config.globalProperties.$strata = strata;
|
|
20
|
+
// Clean up on app unmount
|
|
21
|
+
app.unmount = new Proxy(app.unmount, {
|
|
22
|
+
apply(target, thisArg, argList) {
|
|
23
|
+
strata.close();
|
|
24
|
+
return Reflect.apply(target, thisArg, argList);
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
// Composition API
|
|
30
|
+
/**
|
|
31
|
+
* Use Strata instance
|
|
32
|
+
*/
|
|
33
|
+
export function useStrata(instance) {
|
|
34
|
+
if (instance)
|
|
35
|
+
return instance;
|
|
36
|
+
const strata = inject(StrataKey);
|
|
37
|
+
if (!strata) {
|
|
38
|
+
throw new ValidationError('Strata not provided. Install the plugin (app.use(StrataPlugin)) or, for ' +
|
|
39
|
+
'provider-free usage, pass an instance or use createStrataComposables(instance).', { required: 'app.use(StrataPlugin, { config })' });
|
|
40
|
+
}
|
|
41
|
+
return strata;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Storage composable with reactive state
|
|
45
|
+
*/
|
|
46
|
+
export function useStorage(key, defaultValue, options, instance) {
|
|
47
|
+
const strata = useStrata(instance);
|
|
48
|
+
const value = ref(null);
|
|
49
|
+
const loading = ref(true);
|
|
50
|
+
const error = ref(null);
|
|
51
|
+
// Load initial value
|
|
52
|
+
const refresh = async () => {
|
|
53
|
+
loading.value = true;
|
|
54
|
+
error.value = null;
|
|
55
|
+
try {
|
|
56
|
+
const result = await strata.get(key, options);
|
|
57
|
+
value.value = result ?? defaultValue ?? null;
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
error.value = e;
|
|
61
|
+
value.value = defaultValue ?? null;
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
loading.value = false;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
// Update value
|
|
68
|
+
const update = async (newValue, updateOptions) => {
|
|
69
|
+
loading.value = true;
|
|
70
|
+
error.value = null;
|
|
71
|
+
try {
|
|
72
|
+
if (newValue === null) {
|
|
73
|
+
await strata.remove(key, updateOptions ?? options);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
await strata.set(key, newValue, updateOptions ?? options);
|
|
77
|
+
}
|
|
78
|
+
value.value = newValue;
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
error.value = e;
|
|
82
|
+
throw e;
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
loading.value = false;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
// Remove value
|
|
89
|
+
const remove = async () => {
|
|
90
|
+
await update(null);
|
|
91
|
+
};
|
|
92
|
+
// Subscribe to changes
|
|
93
|
+
onMounted(() => {
|
|
94
|
+
refresh();
|
|
95
|
+
const unsubscribe = strata.subscribe((change) => {
|
|
96
|
+
if (change.key === key) {
|
|
97
|
+
value.value = change.newValue;
|
|
98
|
+
}
|
|
99
|
+
}, options);
|
|
100
|
+
onUnmounted(unsubscribe);
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
value,
|
|
104
|
+
loading,
|
|
105
|
+
error,
|
|
106
|
+
refresh,
|
|
107
|
+
update,
|
|
108
|
+
remove,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Query composable
|
|
113
|
+
*/
|
|
114
|
+
export function useStorageQuery(condition, options, instance) {
|
|
115
|
+
const strata = useStrata(instance);
|
|
116
|
+
const data = ref([]);
|
|
117
|
+
const loading = ref(true);
|
|
118
|
+
const error = ref(null);
|
|
119
|
+
const refetch = async () => {
|
|
120
|
+
loading.value = true;
|
|
121
|
+
error.value = null;
|
|
122
|
+
try {
|
|
123
|
+
const results = await strata.query(condition, options);
|
|
124
|
+
data.value = results;
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
error.value = e;
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
loading.value = false;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
// Watch for condition changes
|
|
134
|
+
watch(() => JSON.stringify(condition), () => refetch(), { immediate: true });
|
|
135
|
+
// Subscribe to any storage changes
|
|
136
|
+
onMounted(() => {
|
|
137
|
+
const unsubscribe = strata.subscribe(() => {
|
|
138
|
+
refetch();
|
|
139
|
+
}, options);
|
|
140
|
+
onUnmounted(unsubscribe);
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
data,
|
|
144
|
+
loading,
|
|
145
|
+
error,
|
|
146
|
+
refetch,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* TTL composable
|
|
151
|
+
*/
|
|
152
|
+
export function useStorageTTL(key, options, instance) {
|
|
153
|
+
const strata = useStrata(instance);
|
|
154
|
+
const milliseconds = ref(null);
|
|
155
|
+
let intervalId;
|
|
156
|
+
const ttl = computed(() => {
|
|
157
|
+
if (milliseconds.value === null || milliseconds.value <= 0) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
return formatTTL(milliseconds.value);
|
|
161
|
+
});
|
|
162
|
+
const updateTTL = async () => {
|
|
163
|
+
try {
|
|
164
|
+
const remaining = await strata.getTTL(key, options);
|
|
165
|
+
milliseconds.value = remaining;
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
milliseconds.value = null;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
const extendTTL = async (extension) => {
|
|
172
|
+
await strata.extendTTL(key, extension, options);
|
|
173
|
+
await updateTTL();
|
|
174
|
+
};
|
|
175
|
+
const persist = async () => {
|
|
176
|
+
await strata.persist(key, options);
|
|
177
|
+
milliseconds.value = null;
|
|
178
|
+
};
|
|
179
|
+
onMounted(() => {
|
|
180
|
+
updateTTL();
|
|
181
|
+
intervalId = setInterval(updateTTL, 1000);
|
|
182
|
+
});
|
|
183
|
+
onUnmounted(() => {
|
|
184
|
+
if (intervalId) {
|
|
185
|
+
clearInterval(intervalId);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
return {
|
|
189
|
+
ttl,
|
|
190
|
+
milliseconds,
|
|
191
|
+
extendTTL,
|
|
192
|
+
persist,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Storage size composable
|
|
197
|
+
*/
|
|
198
|
+
export function useStorageSize(autoRefresh = false, refreshInterval = 5000, instance) {
|
|
199
|
+
const strata = useStrata(instance);
|
|
200
|
+
const size = ref(0);
|
|
201
|
+
const count = ref(0);
|
|
202
|
+
let intervalId;
|
|
203
|
+
const formatted = computed(() => formatBytes(size.value));
|
|
204
|
+
const refresh = async () => {
|
|
205
|
+
const info = await strata.size();
|
|
206
|
+
size.value = info.total;
|
|
207
|
+
count.value = info.count;
|
|
208
|
+
};
|
|
209
|
+
onMounted(() => {
|
|
210
|
+
refresh();
|
|
211
|
+
if (autoRefresh) {
|
|
212
|
+
intervalId = setInterval(refresh, refreshInterval);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
onUnmounted(() => {
|
|
216
|
+
if (intervalId) {
|
|
217
|
+
clearInterval(intervalId);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
return {
|
|
221
|
+
size,
|
|
222
|
+
count,
|
|
223
|
+
formatted,
|
|
224
|
+
refresh,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Bind the Strata composables to a specific instance — the provider-free,
|
|
229
|
+
* no-plugin pattern. Use the returned composables in any component's setup()
|
|
230
|
+
* without installing StrataPlugin.
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```ts
|
|
234
|
+
* import { defineStorage } from 'strata-storage';
|
|
235
|
+
* import { createStrataComposables } from 'strata-storage/vue';
|
|
236
|
+
* const storage = defineStorage();
|
|
237
|
+
* export const { useStorage } = createStrataComposables(storage);
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
export function createStrataComposables(strata) {
|
|
241
|
+
return {
|
|
242
|
+
useStorage: (key, defaultValue, options) => useStorage(key, defaultValue, options, strata),
|
|
243
|
+
useStorageQuery: (condition, options) => useStorageQuery(condition, options, strata),
|
|
244
|
+
useStorageTTL: (key, options) => useStorageTTL(key, options, strata),
|
|
245
|
+
useStorageSize: (autoRefresh, refreshInterval) => useStorageSize(autoRefresh, refreshInterval, strata),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
// Helper functions
|
|
249
|
+
function formatTTL(milliseconds) {
|
|
250
|
+
const seconds = Math.floor(milliseconds / 1000);
|
|
251
|
+
const minutes = Math.floor(seconds / 60);
|
|
252
|
+
const hours = Math.floor(minutes / 60);
|
|
253
|
+
const days = Math.floor(hours / 24);
|
|
254
|
+
if (days > 0) {
|
|
255
|
+
return `${days}d ${hours % 24}h`;
|
|
256
|
+
}
|
|
257
|
+
else if (hours > 0) {
|
|
258
|
+
return `${hours}h ${minutes % 60}m`;
|
|
259
|
+
}
|
|
260
|
+
else if (minutes > 0) {
|
|
261
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
return `${seconds}s`;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function formatBytes(bytes) {
|
|
268
|
+
if (bytes === 0)
|
|
269
|
+
return '0 Bytes';
|
|
270
|
+
const k = 1024;
|
|
271
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
272
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
273
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
274
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# AGENTS.md — ios/
|
|
2
|
+
|
|
3
|
+
Last Updated: 2026-04-03
|
|
4
|
+
|
|
5
|
+
> Agent instructions for iOS native development.
|
|
6
|
+
|
|
7
|
+
## Files
|
|
8
|
+
|
|
9
|
+
| File | Purpose |
|
|
10
|
+
|------|---------|
|
|
11
|
+
| `Plugin/StrataStoragePlugin.swift` | Capacitor plugin entry |
|
|
12
|
+
| `Plugin/UserDefaultsStorage.swift` | General key-value storage |
|
|
13
|
+
| `Plugin/KeychainStorage.swift` | Secure storage |
|
|
14
|
+
| `Plugin/SQLiteStorage.swift` | Database storage (~11KB) |
|
|
15
|
+
|
|
16
|
+
## Agent Rules
|
|
17
|
+
|
|
18
|
+
### Security (IRON-SOLID)
|
|
19
|
+
- Sensitive data MUST use `KeychainStorage`
|
|
20
|
+
- NEVER store secrets in `UserDefaultsStorage`
|
|
21
|
+
|
|
22
|
+
### SQL Safety
|
|
23
|
+
- ALWAYS use parameterized queries in SQLiteStorage
|
|
24
|
+
- NEVER string-interpolate SQL values
|
|
25
|
+
|
|
26
|
+
### Plugin Architecture
|
|
27
|
+
- Methods exposed through `StrataStoragePlugin.swift`
|
|
28
|
+
- Each storage backend is a separate Swift file
|
|
29
|
+
- Bridges Capacitor JS calls to native Swift
|
|
30
|
+
|
|
31
|
+
### Before Modifying
|
|
32
|
+
- Understand Capacitor plugin protocol
|
|
33
|
+
- Test on iOS simulator after changes
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# CLAUDE.md — ios/
|
|
2
|
+
|
|
3
|
+
Last Updated: 2026-04-03
|
|
4
|
+
|
|
5
|
+
## iOS Native Plugin
|
|
6
|
+
|
|
7
|
+
Swift implementation of native storage backends for Capacitor.
|
|
8
|
+
|
|
9
|
+
### Files
|
|
10
|
+
|
|
11
|
+
| File | Purpose |
|
|
12
|
+
|------|---------|
|
|
13
|
+
| `Plugin/StrataStoragePlugin.swift` | Main Capacitor plugin entry point |
|
|
14
|
+
| `Plugin/UserDefaultsStorage.swift` | UserDefaults-based general storage |
|
|
15
|
+
| `Plugin/KeychainStorage.swift` | Keychain-based secure storage |
|
|
16
|
+
| `Plugin/SQLiteStorage.swift` | SQLite database storage (~11KB) |
|
|
17
|
+
|
|
18
|
+
### Configuration
|
|
19
|
+
|
|
20
|
+
- Pod spec: `StrataStorage.podspec` (root)
|
|
21
|
+
- Minimum iOS version: defined in podspec
|
|
22
|
+
- Language: Swift
|
|
23
|
+
|
|
24
|
+
## Rules
|
|
25
|
+
|
|
26
|
+
### Security (IRON-SOLID)
|
|
27
|
+
- Sensitive data MUST use `KeychainStorage`
|
|
28
|
+
- NEVER store credentials, tokens, or secrets in `UserDefaultsStorage`
|
|
29
|
+
- Keychain items should use appropriate access control flags
|
|
30
|
+
|
|
31
|
+
### Plugin Architecture
|
|
32
|
+
- All native methods are exposed through `StrataStoragePlugin.swift`
|
|
33
|
+
- Plugin bridges Capacitor JS calls to native Swift implementations
|
|
34
|
+
- Each storage backend is a separate Swift file
|
|
35
|
+
|
|
36
|
+
### SQLite
|
|
37
|
+
- `SQLiteStorage.swift` is the largest file (~11KB)
|
|
38
|
+
- Handles table creation, migrations, and CRUD operations
|
|
39
|
+
- Use parameterized queries — NEVER string-interpolate SQL
|
|
40
|
+
|
|
41
|
+
### Testing
|
|
42
|
+
- Native code is tested through the Capacitor bridge
|
|
43
|
+
- Test via the demo app on iOS simulator
|
|
44
|
+
- Verify all adapter methods work end-to-end
|
|
45
|
+
|
|
46
|
+
### Before Modifying
|
|
47
|
+
- Understand the Capacitor plugin protocol
|
|
48
|
+
- Test on iOS simulator after changes
|
|
49
|
+
- Verify podspec still resolves
|