strata-storage 2.4.3 → 2.6.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 +51 -0
- package/android/CLAUDE.md +89 -0
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/strata/storage/FilesystemStorage.java +287 -0
- package/android/src/main/java/com/strata/storage/SQLiteStorage.java +260 -203
- package/android/src/main/java/com/stratastorage/StrataStoragePlugin.java +357 -69
- 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 +51 -0
- package/dist/android/CLAUDE.md +89 -0
- package/dist/android/build.gradle +1 -1
- package/dist/android/src/main/java/com/strata/storage/FilesystemStorage.java +287 -0
- package/dist/android/src/main/java/com/strata/storage/SQLiteStorage.java +260 -203
- package/dist/android/src/main/java/com/stratastorage/StrataStoragePlugin.java +357 -69
- 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 +48 -0
- package/dist/ios/CLAUDE.md +84 -0
- package/dist/ios/Plugin/FilesystemStorage.swift +218 -0
- package/dist/ios/Plugin/KeychainStorage.swift +139 -50
- package/dist/ios/Plugin/SQLiteStorage.swift +279 -147
- package/dist/ios/Plugin/StrataStoragePlugin.m +23 -0
- package/dist/ios/Plugin/StrataStoragePlugin.swift +272 -65
- 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 +48 -0
- package/ios/CLAUDE.md +84 -0
- package/ios/Plugin/FilesystemStorage.swift +218 -0
- package/ios/Plugin/KeychainStorage.swift +139 -50
- package/ios/Plugin/SQLiteStorage.swift +279 -147
- package/ios/Plugin/StrataStoragePlugin.m +23 -0
- package/ios/Plugin/StrataStoragePlugin.swift +272 -65
- 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,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL Adapter - persists state in the page URL.
|
|
3
|
+
*
|
|
4
|
+
* Stores each key as a URL parameter, either in the query string (`?k=v`) or in
|
|
5
|
+
* the hash fragment (`#k=v`), selectable via config. Useful for shareable /
|
|
6
|
+
* bookmarkable UI state (filters, tabs, pagination) that should survive reloads
|
|
7
|
+
* and round-trip through navigation. Inherently synchronous.
|
|
8
|
+
*
|
|
9
|
+
* Limitations (documented honestly):
|
|
10
|
+
* - URLs have practical length limits (~2000 chars in some browsers/servers), so
|
|
11
|
+
* this adapter is for small, simple, serializable state — not bulk data.
|
|
12
|
+
* - Query mode is visible to the server on navigation; hash mode is client-only.
|
|
13
|
+
* - Not available outside a browser (SSR/Node) — `isAvailable()` returns false.
|
|
14
|
+
*/
|
|
15
|
+
import { BaseAdapter } from '@/core/BaseAdapter';
|
|
16
|
+
import type { StorageType, StorageCapabilities, StorageValue, ClearOptions } from '@/types';
|
|
17
|
+
/** Configuration for the URL adapter. */
|
|
18
|
+
export interface URLAdapterConfig {
|
|
19
|
+
/** Where to store params: query string (default) or hash fragment. */
|
|
20
|
+
mode?: 'query' | 'hash';
|
|
21
|
+
/** Prefix applied to every param name to avoid collisions (default `strata.`). */
|
|
22
|
+
prefix?: string;
|
|
23
|
+
/** History strategy when writing (default `replace` — no new history entry). */
|
|
24
|
+
history?: 'push' | 'replace';
|
|
25
|
+
/** Soft warning threshold for total URL length in chars (default 2000). */
|
|
26
|
+
maxLength?: number;
|
|
27
|
+
}
|
|
28
|
+
export declare class URLAdapter extends BaseAdapter {
|
|
29
|
+
readonly name: StorageType;
|
|
30
|
+
readonly capabilities: StorageCapabilities;
|
|
31
|
+
private mode;
|
|
32
|
+
private prefix;
|
|
33
|
+
private historyMode;
|
|
34
|
+
private maxLength;
|
|
35
|
+
private snapshot;
|
|
36
|
+
private changeListener?;
|
|
37
|
+
isAvailable(): Promise<boolean>;
|
|
38
|
+
initialize(config?: URLAdapterConfig): Promise<void>;
|
|
39
|
+
getSync<T = unknown>(key: string): StorageValue<T> | null;
|
|
40
|
+
setSync<T = unknown>(key: string, value: StorageValue<T>): void;
|
|
41
|
+
removeSync(key: string): void;
|
|
42
|
+
hasSync(key: string): boolean;
|
|
43
|
+
keysSync(pattern?: string | RegExp): string[];
|
|
44
|
+
clearSync(options?: ClearOptions): void;
|
|
45
|
+
get<T = unknown>(key: string): Promise<StorageValue<T> | null>;
|
|
46
|
+
set<T = unknown>(key: string, value: StorageValue<T>): Promise<void>;
|
|
47
|
+
remove(key: string): Promise<void>;
|
|
48
|
+
has(key: string): Promise<boolean>;
|
|
49
|
+
keys(pattern?: string | RegExp): Promise<string[]>;
|
|
50
|
+
clear(options?: ClearOptions): Promise<void>;
|
|
51
|
+
close(): Promise<void>;
|
|
52
|
+
private readParams;
|
|
53
|
+
private writeParams;
|
|
54
|
+
private estimateLength;
|
|
55
|
+
private tryDeserialize;
|
|
56
|
+
private snapshotParams;
|
|
57
|
+
private handleExternalChange;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=URLAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"URLAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/web/URLAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI5F,yCAAyC;AACzC,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,UAAW,SAAQ,WAAW;IACzC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAS;IACnC,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAUxC;IAEF,OAAO,CAAC,IAAI,CAA6B;IACzC,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,cAAc,CAAC,CAAa;IAE9B,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAQ/B,UAAU,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB1D,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI;IAmBzD,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI;IAmB/D,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAS7B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAK7B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE;IAU7C,SAAS,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI;IAoCjC,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAI9D,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIlC,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAIlD,KAAK,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAW5B,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,WAAW;IAkBnB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,oBAAoB;CAoB7B"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL Adapter - persists state in the page URL.
|
|
3
|
+
*
|
|
4
|
+
* Stores each key as a URL parameter, either in the query string (`?k=v`) or in
|
|
5
|
+
* the hash fragment (`#k=v`), selectable via config. Useful for shareable /
|
|
6
|
+
* bookmarkable UI state (filters, tabs, pagination) that should survive reloads
|
|
7
|
+
* and round-trip through navigation. Inherently synchronous.
|
|
8
|
+
*
|
|
9
|
+
* Limitations (documented honestly):
|
|
10
|
+
* - URLs have practical length limits (~2000 chars in some browsers/servers), so
|
|
11
|
+
* this adapter is for small, simple, serializable state — not bulk data.
|
|
12
|
+
* - Query mode is visible to the server on navigation; hash mode is client-only.
|
|
13
|
+
* - Not available outside a browser (SSR/Node) — `isAvailable()` returns false.
|
|
14
|
+
*/
|
|
15
|
+
import { BaseAdapter } from "../../core/BaseAdapter.js";
|
|
16
|
+
import { serialize, deserialize } from "../../utils/index.js";
|
|
17
|
+
import { logger } from "../../utils/logger.js";
|
|
18
|
+
export class URLAdapter extends BaseAdapter {
|
|
19
|
+
name = 'url';
|
|
20
|
+
capabilities = {
|
|
21
|
+
persistent: false, // lives only as long as the URL does
|
|
22
|
+
synchronous: true, // URL access is synchronous
|
|
23
|
+
observable: true, // popstate / hashchange
|
|
24
|
+
transactional: false,
|
|
25
|
+
queryable: true, // via BaseAdapter scan
|
|
26
|
+
maxSize: 2000, // practical URL length budget
|
|
27
|
+
binary: false,
|
|
28
|
+
encrypted: false,
|
|
29
|
+
crossTab: false,
|
|
30
|
+
};
|
|
31
|
+
mode = 'query';
|
|
32
|
+
prefix = 'strata.';
|
|
33
|
+
historyMode = 'replace';
|
|
34
|
+
maxLength = 2000;
|
|
35
|
+
snapshot = new Map();
|
|
36
|
+
changeListener;
|
|
37
|
+
async isAvailable() {
|
|
38
|
+
return (typeof window !== 'undefined' &&
|
|
39
|
+
typeof window.location !== 'undefined' &&
|
|
40
|
+
typeof window.history !== 'undefined');
|
|
41
|
+
}
|
|
42
|
+
async initialize(config) {
|
|
43
|
+
if (config?.mode)
|
|
44
|
+
this.mode = config.mode;
|
|
45
|
+
if (config?.prefix !== undefined)
|
|
46
|
+
this.prefix = config.prefix;
|
|
47
|
+
if (config?.history)
|
|
48
|
+
this.historyMode = config.history;
|
|
49
|
+
if (typeof config?.maxLength === 'number')
|
|
50
|
+
this.maxLength = config.maxLength;
|
|
51
|
+
if (typeof window === 'undefined')
|
|
52
|
+
return;
|
|
53
|
+
// Seed the snapshot and listen for external navigation (back/forward, manual
|
|
54
|
+
// edits) so subscribers are notified of changes that did not originate here.
|
|
55
|
+
this.snapshot = this.snapshotParams();
|
|
56
|
+
this.changeListener = () => this.handleExternalChange();
|
|
57
|
+
const event = this.mode === 'hash' ? 'hashchange' : 'popstate';
|
|
58
|
+
window.addEventListener(event, this.changeListener);
|
|
59
|
+
}
|
|
60
|
+
// --- Synchronous core ------------------------------------------------------
|
|
61
|
+
getSync(key) {
|
|
62
|
+
const raw = this.readParams().get(this.prefix + key);
|
|
63
|
+
if (raw === null)
|
|
64
|
+
return null;
|
|
65
|
+
let value;
|
|
66
|
+
try {
|
|
67
|
+
value = deserialize(raw);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
logger.debug(`URLAdapter: failed to parse key "${key}"`, error);
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
if (this.isExpired(value)) {
|
|
74
|
+
this.removeSync(key);
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
setSync(key, value) {
|
|
80
|
+
const params = this.readParams();
|
|
81
|
+
const oldRaw = params.get(this.prefix + key);
|
|
82
|
+
const serialized = serialize(value);
|
|
83
|
+
params.set(this.prefix + key, serialized);
|
|
84
|
+
this.writeParams(params);
|
|
85
|
+
const total = this.estimateLength(params);
|
|
86
|
+
if (total > this.maxLength) {
|
|
87
|
+
logger.debug(`URLAdapter: URL length (${total}) exceeds soft limit (${this.maxLength}); ` +
|
|
88
|
+
`consider a different storage for large values.`);
|
|
89
|
+
}
|
|
90
|
+
const oldValue = oldRaw !== null ? this.tryDeserialize(oldRaw) : undefined;
|
|
91
|
+
this.emitChange(key, oldValue, value.value, 'local');
|
|
92
|
+
}
|
|
93
|
+
removeSync(key) {
|
|
94
|
+
const params = this.readParams();
|
|
95
|
+
const oldRaw = params.get(this.prefix + key);
|
|
96
|
+
if (oldRaw === null)
|
|
97
|
+
return;
|
|
98
|
+
params.delete(this.prefix + key);
|
|
99
|
+
this.writeParams(params);
|
|
100
|
+
this.emitChange(key, this.tryDeserialize(oldRaw), undefined, 'local');
|
|
101
|
+
}
|
|
102
|
+
hasSync(key) {
|
|
103
|
+
const value = this.getSync(key);
|
|
104
|
+
return value !== null;
|
|
105
|
+
}
|
|
106
|
+
keysSync(pattern) {
|
|
107
|
+
const keys = [];
|
|
108
|
+
for (const name of this.readParams().keys()) {
|
|
109
|
+
if (name.startsWith(this.prefix)) {
|
|
110
|
+
keys.push(name.slice(this.prefix.length));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return this.filterKeys(keys, pattern);
|
|
114
|
+
}
|
|
115
|
+
clearSync(options) {
|
|
116
|
+
const pattern = options?.pattern || options?.prefix;
|
|
117
|
+
const params = this.readParams();
|
|
118
|
+
let changed = false;
|
|
119
|
+
for (const key of this.keysSync()) {
|
|
120
|
+
let shouldDelete = true;
|
|
121
|
+
if (pattern) {
|
|
122
|
+
shouldDelete = this.filterKeys([key], pattern).length > 0;
|
|
123
|
+
}
|
|
124
|
+
if (shouldDelete && options?.tags) {
|
|
125
|
+
const value = this.getSync(key);
|
|
126
|
+
if (!value?.tags || !options.tags.some((tag) => value.tags?.includes(tag))) {
|
|
127
|
+
shouldDelete = false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (shouldDelete && options?.expiredOnly) {
|
|
131
|
+
const value = this.getSync(key);
|
|
132
|
+
if (!value || !this.isExpired(value)) {
|
|
133
|
+
shouldDelete = false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (shouldDelete) {
|
|
137
|
+
params.delete(this.prefix + key);
|
|
138
|
+
changed = true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (changed) {
|
|
142
|
+
this.writeParams(params);
|
|
143
|
+
this.emitChange('*', undefined, undefined, 'local');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// --- Async API delegates to the synchronous core ---------------------------
|
|
147
|
+
async get(key) {
|
|
148
|
+
return this.getSync(key);
|
|
149
|
+
}
|
|
150
|
+
async set(key, value) {
|
|
151
|
+
this.setSync(key, value);
|
|
152
|
+
}
|
|
153
|
+
async remove(key) {
|
|
154
|
+
this.removeSync(key);
|
|
155
|
+
}
|
|
156
|
+
async has(key) {
|
|
157
|
+
return this.hasSync(key);
|
|
158
|
+
}
|
|
159
|
+
async keys(pattern) {
|
|
160
|
+
return this.keysSync(pattern);
|
|
161
|
+
}
|
|
162
|
+
async clear(options) {
|
|
163
|
+
this.clearSync(options);
|
|
164
|
+
}
|
|
165
|
+
async close() {
|
|
166
|
+
if (this.changeListener && typeof window !== 'undefined') {
|
|
167
|
+
const event = this.mode === 'hash' ? 'hashchange' : 'popstate';
|
|
168
|
+
window.removeEventListener(event, this.changeListener);
|
|
169
|
+
this.changeListener = undefined;
|
|
170
|
+
}
|
|
171
|
+
await super.close();
|
|
172
|
+
}
|
|
173
|
+
// --- Internals -------------------------------------------------------------
|
|
174
|
+
readParams() {
|
|
175
|
+
if (typeof window === 'undefined')
|
|
176
|
+
return new URLSearchParams();
|
|
177
|
+
if (this.mode === 'hash') {
|
|
178
|
+
const hash = window.location.hash.replace(/^#/, '');
|
|
179
|
+
return new URLSearchParams(hash);
|
|
180
|
+
}
|
|
181
|
+
return new URLSearchParams(window.location.search);
|
|
182
|
+
}
|
|
183
|
+
writeParams(params) {
|
|
184
|
+
if (typeof window === 'undefined')
|
|
185
|
+
return;
|
|
186
|
+
const query = params.toString();
|
|
187
|
+
if (this.mode === 'hash') {
|
|
188
|
+
const url = `${window.location.pathname}${window.location.search}${query ? `#${query}` : ''}`;
|
|
189
|
+
window.history[this.historyMode === 'push' ? 'pushState' : 'replaceState']({}, '', url);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
const url = new URL(window.location.href);
|
|
193
|
+
url.search = query;
|
|
194
|
+
window.history[this.historyMode === 'push' ? 'pushState' : 'replaceState']({}, '', url.toString());
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
estimateLength(params) {
|
|
198
|
+
if (typeof window === 'undefined')
|
|
199
|
+
return params.toString().length;
|
|
200
|
+
return `${window.location.origin}${window.location.pathname}?${params.toString()}`.length;
|
|
201
|
+
}
|
|
202
|
+
tryDeserialize(raw) {
|
|
203
|
+
try {
|
|
204
|
+
return deserialize(raw).value;
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// Diff the URL against the last known snapshot and emit per-key changes for
|
|
211
|
+
// anything that changed externally (back/forward navigation, manual edits).
|
|
212
|
+
snapshotParams() {
|
|
213
|
+
const map = new Map();
|
|
214
|
+
for (const [name, value] of this.readParams().entries()) {
|
|
215
|
+
map.set(name, value);
|
|
216
|
+
}
|
|
217
|
+
return map;
|
|
218
|
+
}
|
|
219
|
+
handleExternalChange() {
|
|
220
|
+
const next = this.snapshotParams();
|
|
221
|
+
const allNames = new Set([...this.snapshot.keys(), ...next.keys()]);
|
|
222
|
+
for (const name of allNames) {
|
|
223
|
+
if (!name.startsWith(this.prefix))
|
|
224
|
+
continue;
|
|
225
|
+
const before = this.snapshot.get(name);
|
|
226
|
+
const after = next.get(name);
|
|
227
|
+
if (before === after)
|
|
228
|
+
continue;
|
|
229
|
+
const key = name.slice(this.prefix.length);
|
|
230
|
+
this.emitChange(key, before !== undefined ? this.tryDeserialize(before) : undefined, after !== undefined ? this.tryDeserialize(after) : undefined, 'remote');
|
|
231
|
+
}
|
|
232
|
+
this.snapshot = next;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
@@ -7,4 +7,5 @@ export { SessionStorageAdapter } from './SessionStorageAdapter';
|
|
|
7
7
|
export { IndexedDBAdapter } from './IndexedDBAdapter';
|
|
8
8
|
export { CookieAdapter } from './CookieAdapter';
|
|
9
9
|
export { CacheAdapter } from './CacheAdapter';
|
|
10
|
+
export { URLAdapter, type URLAdapterConfig } from './URLAdapter';
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/web/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/web/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -7,3 +7,4 @@ export { SessionStorageAdapter } from "./SessionStorageAdapter.js";
|
|
|
7
7
|
export { IndexedDBAdapter } from "./IndexedDBAdapter.js";
|
|
8
8
|
export { CookieAdapter } from "./CookieAdapter.js";
|
|
9
9
|
export { CacheAdapter } from "./CacheAdapter.js";
|
|
10
|
+
export { URLAdapter } from "./URLAdapter.js";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# AGENTS.md — android/
|
|
2
|
+
|
|
3
|
+
Last Updated: 2026-05-27
|
|
4
|
+
|
|
5
|
+
> Agent instructions for Android native development.
|
|
6
|
+
|
|
7
|
+
## Files
|
|
8
|
+
|
|
9
|
+
| File | Purpose |
|
|
10
|
+
|------|---------|
|
|
11
|
+
| `StrataStoragePlugin.java` | Capacitor plugin entry (`com.stratastorage`) |
|
|
12
|
+
| `SharedPreferencesStorage.java` | General key-value storage (`com.strata.storage`) |
|
|
13
|
+
| `EncryptedStorage.java` | Secure storage via EncryptedSharedPreferences (`com.strata.storage`); requires `androidx.security:security-crypto 1.1.0` (stable, upgraded in v2.6.0) |
|
|
14
|
+
| `SQLiteStorage.java` | SQLite database storage — multi-store (v2.6.0): `database`/`table` options honoured, full `StorageValue` wrapper round-trip, `size(detailed)` supported (`com.strata.storage`) |
|
|
15
|
+
| `FilesystemStorage.java` | File-per-key native storage under `getFilesDir()/strata_storage/` (new in v2.6.0); atomic writes via staging rename; `isAvailable()` returns `true`; no external permissions needed (`com.strata.storage`) |
|
|
16
|
+
|
|
17
|
+
## v2.6.0 Notes
|
|
18
|
+
|
|
19
|
+
- **`androidx.security 1.1.0`:** upgraded from `1.1.0-alpha06`. Requires `compileSdkVersion 34`. If Gradle sync fails, bump `compileSdkVersion` and `targetSdkVersion` to 34 in `android/app/build.gradle`.
|
|
20
|
+
- **SQLite multi-store:** `call.getString("database")` and `call.getString("table")` are now read and used. Each `(database, table)` pair → distinct `.db` file. Previously ignored.
|
|
21
|
+
- **SQLite value shape:** native `get` returns full `StorageValue` wrapper. Corrupt rows → treated as miss (no throw).
|
|
22
|
+
- **FilesystemStorage.java:** new class. Writes are atomic (staging temp → `renameTo`). `keys()` excludes `.staging/` artifacts. `size(detailed)` → `{ keys, values, metadata }`.
|
|
23
|
+
- **Pending on-device verification** — see `docs/guides/platforms/device-verification.md`.
|
|
24
|
+
|
|
25
|
+
## Agent Rules
|
|
26
|
+
|
|
27
|
+
### Security (IRON-SOLID)
|
|
28
|
+
- Sensitive data MUST use `EncryptedStorage`
|
|
29
|
+
- NEVER store secrets in `SharedPreferencesStorage`
|
|
30
|
+
- `androidx.security:security-crypto` version MUST be `1.1.0` (stable) — not alpha
|
|
31
|
+
|
|
32
|
+
### SQL Safety (IRON-SOLID)
|
|
33
|
+
- ALWAYS use parameterized queries
|
|
34
|
+
- NEVER concatenate user input into SQL strings
|
|
35
|
+
|
|
36
|
+
### Filesystem Safety
|
|
37
|
+
- NEVER read from `strata_storage/.staging/` — staging files are transient
|
|
38
|
+
- Key sanitisation must be consistent between `set`, `get`, `remove`, and `keys`
|
|
39
|
+
|
|
40
|
+
### Plugin Architecture
|
|
41
|
+
- Methods exposed through `StrataStoragePlugin.java`
|
|
42
|
+
- Each storage backend is a separate Java class
|
|
43
|
+
- Two package paths: `com.stratastorage` (plugin) and `com.strata.storage` (impl)
|
|
44
|
+
|
|
45
|
+
### Build
|
|
46
|
+
- `compileSdkVersion 34` required for `androidx.security 1.1.0`
|
|
47
|
+
- Verify Gradle build succeeds after any change
|
|
48
|
+
|
|
49
|
+
### Before Modifying
|
|
50
|
+
- Understand Capacitor plugin protocol for Android
|
|
51
|
+
- Test on Android emulator after changes; follow device-verification guide
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# CLAUDE.md — android/
|
|
2
|
+
|
|
3
|
+
Last Updated: 2026-05-27
|
|
4
|
+
|
|
5
|
+
## Android Native Plugin
|
|
6
|
+
|
|
7
|
+
Java implementation of native storage backends for Capacitor.
|
|
8
|
+
|
|
9
|
+
### Files
|
|
10
|
+
|
|
11
|
+
| File | Path | Purpose |
|
|
12
|
+
|------|------|---------|
|
|
13
|
+
| `StrataStoragePlugin.java` | `src/main/java/com/stratastorage/` | Main Capacitor plugin entry |
|
|
14
|
+
| `SharedPreferencesStorage.java` | `src/main/java/com/strata/storage/` | SharedPreferences general storage |
|
|
15
|
+
| `EncryptedStorage.java` | `src/main/java/com/strata/storage/` | EncryptedSharedPreferences secure storage (`androidx.security 1.1.0`) |
|
|
16
|
+
| `SQLiteStorage.java` | `src/main/java/com/strata/storage/` | SQLite database storage — multi-store (v2.6.0) |
|
|
17
|
+
| `FilesystemStorage.java` | `src/main/java/com/strata/storage/` | File-per-key storage under `Context.getFilesDir()/strata_storage/` (new in v2.6.0) |
|
|
18
|
+
|
|
19
|
+
### Configuration
|
|
20
|
+
|
|
21
|
+
- Build: `build.gradle`
|
|
22
|
+
- Proguard: `proguard-rules.pro`
|
|
23
|
+
- Manifest: `src/main/AndroidManifest.xml`
|
|
24
|
+
|
|
25
|
+
**Note**: There are two Java package paths — `com.stratastorage` (plugin) and `com.strata.storage` (implementations).
|
|
26
|
+
|
|
27
|
+
## v2.6.0 Changes
|
|
28
|
+
|
|
29
|
+
### `androidx.security:security-crypto` 1.1.0 (stable)
|
|
30
|
+
`EncryptedStorage.java` depends on `androidx.security:security-crypto`. The
|
|
31
|
+
dependency was upgraded from `1.1.0-alpha06` to `1.1.0` (stable) in v2.6.0.
|
|
32
|
+
The `EncryptedSharedPreferences` / `MasterKey` API is unchanged. The stable
|
|
33
|
+
version requires `compileSdkVersion 34` — if Gradle sync fails after this
|
|
34
|
+
change, bump `compileSdkVersion` and `targetSdkVersion` in
|
|
35
|
+
`android/app/build.gradle` to 34.
|
|
36
|
+
|
|
37
|
+
### SQLite multi-store
|
|
38
|
+
`SQLiteStorage.java` now accepts `database` and `table` parameters from
|
|
39
|
+
`call.getString("database")` / `call.getString("table")`. Each unique pair
|
|
40
|
+
opens a distinct `.db` file in the app's internal storage. Table identifiers
|
|
41
|
+
are sanitised to `[A-Za-z0-9_]`. The full `StorageValue` wrapper is serialised
|
|
42
|
+
to JSON and stored in a single `value` column; native `get` returns the full
|
|
43
|
+
wrapper so TTL, tags, and metadata survive the round-trip. `size(detailed)`
|
|
44
|
+
returns per-column byte breakdown.
|
|
45
|
+
|
|
46
|
+
### FilesystemStorage.java (new)
|
|
47
|
+
Stores each key as `getFilesDir()/strata_storage/<sanitised-key>.json`. Writes
|
|
48
|
+
are atomic: value is written to `strata_storage/.staging/<key>.tmp`, then
|
|
49
|
+
renamed into place using `renameTo()`. Staging artifacts are excluded from
|
|
50
|
+
`keys()`. No external filesystem permissions are needed (`getFilesDir()` is
|
|
51
|
+
app-private). `isAvailable()` returns `true`. `size(detailed)` supported.
|
|
52
|
+
|
|
53
|
+
> **Pending on-device verification** — native code is complete and reviewed; see
|
|
54
|
+
> `docs/guides/platforms/device-verification.md` for the test matrix.
|
|
55
|
+
|
|
56
|
+
## Rules
|
|
57
|
+
|
|
58
|
+
### Security (IRON-SOLID)
|
|
59
|
+
- Sensitive data MUST use `EncryptedStorage` (EncryptedSharedPreferences)
|
|
60
|
+
- NEVER store credentials, tokens, or secrets in `SharedPreferencesStorage`
|
|
61
|
+
- Follow Android Keystore best practices
|
|
62
|
+
- `androidx.security:security-crypto 1.1.0` (stable) is the required version
|
|
63
|
+
|
|
64
|
+
### SQL Safety (IRON-SOLID)
|
|
65
|
+
- ALWAYS use parameterized queries in `SQLiteStorage`
|
|
66
|
+
- NEVER concatenate user input into SQL strings
|
|
67
|
+
- Use `SQLiteDatabase.query()` or prepared statements
|
|
68
|
+
|
|
69
|
+
### Filesystem Safety
|
|
70
|
+
- NEVER read from `strata_storage/.staging/` — staging files are transient
|
|
71
|
+
- Key sanitisation must be consistent between `set`, `get`, `remove`, and `keys`
|
|
72
|
+
- `getFilesDir()` is app-private; no external storage permissions required
|
|
73
|
+
|
|
74
|
+
### Plugin Architecture
|
|
75
|
+
- All native methods exposed through `StrataStoragePlugin.java`
|
|
76
|
+
- Plugin bridges Capacitor JS calls to Java implementations
|
|
77
|
+
- Each storage backend is a separate Java class
|
|
78
|
+
- Two package paths: `com.stratastorage` (plugin) and `com.strata.storage` (impl)
|
|
79
|
+
|
|
80
|
+
### Build
|
|
81
|
+
- Gradle-based build system
|
|
82
|
+
- `compileSdkVersion 34` required for `androidx.security 1.1.0`
|
|
83
|
+
- Proguard rules configured for release builds
|
|
84
|
+
- Test on Android emulator after changes; follow device-verification guide
|
|
85
|
+
|
|
86
|
+
### Before Modifying
|
|
87
|
+
- Understand the Capacitor plugin protocol for Android
|
|
88
|
+
- Test on Android emulator after changes
|
|
89
|
+
- Verify Gradle build succeeds
|
|
@@ -51,7 +51,7 @@ dependencies {
|
|
|
51
51
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
52
52
|
implementation project(':capacitor-android')
|
|
53
53
|
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
54
|
-
implementation 'androidx.security:security-crypto:1.1.0
|
|
54
|
+
implementation 'androidx.security:security-crypto:1.1.0'
|
|
55
55
|
testImplementation "junit:junit:$junitVersion"
|
|
56
56
|
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
57
57
|
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|