@biglogic/rgs 2.9.2 → 2.9.5
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/README.md +28 -2
- package/SECURITY.md +10 -0
- package/advanced.js +1 -1
- package/core/types.d.ts +4 -0
- package/examples/README.md +41 -0
- package/examples/async-data-fetch/UserLoader.d.ts +12 -0
- package/examples/async-data-fetch/UserLoader.ts +31 -0
- package/examples/basic-counter/CounterComponent.d.ts +2 -0
- package/examples/basic-counter/CounterComponent.tsx +22 -0
- package/examples/basic-counter/CounterStore.d.ts +7 -0
- package/examples/basic-counter/CounterStore.ts +27 -0
- package/examples/big-data-indexeddb/BigDataStore.d.ts +10 -0
- package/examples/big-data-indexeddb/BigDataStore.ts +60 -0
- package/examples/global-theme/ThemeManager.d.ts +7 -0
- package/examples/global-theme/ThemeManager.ts +32 -0
- package/examples/hybrid-cloud-sync/HybridStore.d.ts +19 -0
- package/examples/hybrid-cloud-sync/HybridStore.ts +78 -0
- package/examples/persistent-cart/CartStore.d.ts +13 -0
- package/examples/persistent-cart/CartStore.ts +48 -0
- package/examples/rbac-dashboard/DashboardStore.d.ts +47 -0
- package/examples/rbac-dashboard/DashboardStore.ts +46 -0
- package/examples/secure-auth/AuthStore.d.ts +14 -0
- package/examples/secure-auth/AuthStore.ts +36 -0
- package/examples/security-best-practices/SecurityStore.d.ts +21 -0
- package/examples/security-best-practices/SecurityStore.ts +73 -0
- package/examples/stress-tests/StressStore.d.ts +41 -0
- package/examples/stress-tests/StressStore.ts +61 -0
- package/examples/undo-redo-editor/EditorStore.d.ts +9 -0
- package/examples/undo-redo-editor/EditorStore.ts +29 -0
- package/index.d.ts +2 -2
- package/index.js +1 -1
- package/markdown/SUMMARY.md +4 -0
- package/markdown/api.md +40 -1
- package/markdown/chapters/02-getting-started.md +1 -1
- package/markdown/chapters/03-the-magnetar-way.md +10 -3
- package/markdown/chapters/04-persistence-and-safety.md +46 -5
- package/markdown/chapters/05-plugins-and-extensibility.md +24 -8
- package/markdown/chapters/06-case-studies.md +69 -69
- package/markdown/chapters/08-migration-guide.md +49 -2
- package/markdown/chapters/09-security-architecture.md +40 -0
- package/package.json +9 -4
- package/plugins/index.d.ts +2 -0
- package/plugins/official/cloud-sync.plugin.d.ts +22 -0
- package/plugins/official/indexeddb.plugin.d.ts +7 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { gstate } from '../../index'
|
|
2
|
+
|
|
3
|
+
export interface CartItem {
|
|
4
|
+
id: string
|
|
5
|
+
name: string
|
|
6
|
+
price: number
|
|
7
|
+
quantity: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface CartState extends Record<string, unknown> {
|
|
11
|
+
items: CartItem[]
|
|
12
|
+
coupon: string | null
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Persistent Cart Store
|
|
17
|
+
* RECOMMENDED FOR: Frontend (FE)
|
|
18
|
+
*/
|
|
19
|
+
export const useCart = gstate<CartState>({
|
|
20
|
+
items: [],
|
|
21
|
+
coupon: null
|
|
22
|
+
}, {
|
|
23
|
+
namespace: 'shopping-cart',
|
|
24
|
+
persist: true
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
export const addToCart = (product: Omit<CartItem, 'quantity'>) => {
|
|
28
|
+
useCart.transaction(() => {
|
|
29
|
+
const items = useCart.get('items') || []
|
|
30
|
+
const existingIndex = items.findIndex(item => item.id === product.id)
|
|
31
|
+
|
|
32
|
+
if (existingIndex > -1) {
|
|
33
|
+
const newItems = [...items]
|
|
34
|
+
const item = newItems[existingIndex]
|
|
35
|
+
if (item) {
|
|
36
|
+
newItems[existingIndex] = { ...item, quantity: item.quantity + 1 }
|
|
37
|
+
useCart.set('items', newItems)
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
useCart.set('items', [...items, { ...product, quantity: 1 }])
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const getCartTotal = () => useCart.compute('totalPrice', (get) => {
|
|
46
|
+
const items = get<CartItem[]>('items') || []
|
|
47
|
+
return items.reduce((total, item) => total + (item.price * item.quantity), 0)
|
|
48
|
+
})
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export declare const useDashboard: import("../../advanced").IStore<{
|
|
2
|
+
stats: {
|
|
3
|
+
visitors: number;
|
|
4
|
+
revenue: number;
|
|
5
|
+
};
|
|
6
|
+
settings: {
|
|
7
|
+
allowPublicSignup: boolean;
|
|
8
|
+
};
|
|
9
|
+
logs: string[];
|
|
10
|
+
}> & (<K extends "stats" | "settings" | "logs">(key: K) => readonly [{
|
|
11
|
+
stats: {
|
|
12
|
+
visitors: number;
|
|
13
|
+
revenue: number;
|
|
14
|
+
};
|
|
15
|
+
settings: {
|
|
16
|
+
allowPublicSignup: boolean;
|
|
17
|
+
};
|
|
18
|
+
logs: string[];
|
|
19
|
+
}[K] | undefined, (val: {
|
|
20
|
+
stats: {
|
|
21
|
+
visitors: number;
|
|
22
|
+
revenue: number;
|
|
23
|
+
};
|
|
24
|
+
settings: {
|
|
25
|
+
allowPublicSignup: boolean;
|
|
26
|
+
};
|
|
27
|
+
logs: string[];
|
|
28
|
+
}[K] | ((draft: {
|
|
29
|
+
stats: {
|
|
30
|
+
visitors: number;
|
|
31
|
+
revenue: number;
|
|
32
|
+
};
|
|
33
|
+
settings: {
|
|
34
|
+
allowPublicSignup: boolean;
|
|
35
|
+
};
|
|
36
|
+
logs: string[];
|
|
37
|
+
}[K]) => {
|
|
38
|
+
stats: {
|
|
39
|
+
visitors: number;
|
|
40
|
+
revenue: number;
|
|
41
|
+
};
|
|
42
|
+
settings: {
|
|
43
|
+
allowPublicSignup: boolean;
|
|
44
|
+
};
|
|
45
|
+
logs: string[];
|
|
46
|
+
}[K]), options?: unknown) => boolean]);
|
|
47
|
+
export declare const addLog: (msg: string) => boolean;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { gstate } from '../../index'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* RBAC Dashboard Store
|
|
5
|
+
* RECOMMENDED FOR: Backend (BE) / Admin Frontend (FE)
|
|
6
|
+
*
|
|
7
|
+
* Demonstrates Role-Based Access Control.
|
|
8
|
+
*/
|
|
9
|
+
export const useDashboard = gstate({
|
|
10
|
+
stats: { visitors: 100, revenue: 5000 },
|
|
11
|
+
settings: { allowPublicSignup: true },
|
|
12
|
+
logs: [] as string[]
|
|
13
|
+
}, {
|
|
14
|
+
userId: 'manager-101',
|
|
15
|
+
accessRules: [
|
|
16
|
+
{
|
|
17
|
+
// Allow 'manager-101' read access to stats
|
|
18
|
+
pattern: (key, userId) => key === 'stats' && userId === 'manager-101',
|
|
19
|
+
permissions: ['read']
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
// Allow 'manager-101' full access to settings
|
|
23
|
+
pattern: (key, userId) => key === 'settings' && userId === 'manager-101',
|
|
24
|
+
permissions: ['read', 'write']
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
// ONLY 'admin-user' can access logs
|
|
28
|
+
pattern: (key, userId) => key === 'logs' && userId === 'admin-user',
|
|
29
|
+
permissions: ['admin']
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Attempt to add a log entry.
|
|
36
|
+
* Will fail if the current store userId is not 'admin-user'.
|
|
37
|
+
*/
|
|
38
|
+
export const addLog = (msg: string) => {
|
|
39
|
+
const success = useDashboard.set('logs', (draft: string[]) => {
|
|
40
|
+
draft.push(msg)
|
|
41
|
+
})
|
|
42
|
+
if (!success) {
|
|
43
|
+
console.warn(`[Security] Access Denied for user ${useDashboard.userId}: Insufficient permissions for 'logs'`)
|
|
44
|
+
}
|
|
45
|
+
return success
|
|
46
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface AuthState extends Record<string, unknown> {
|
|
2
|
+
user: {
|
|
3
|
+
id: string;
|
|
4
|
+
email: string;
|
|
5
|
+
} | null;
|
|
6
|
+
token: string | null;
|
|
7
|
+
isAuthenticated: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare const useAuth: import("../../advanced").IStore<AuthState> & (<K extends keyof AuthState>(key: K) => readonly [AuthState[K] | undefined, (val: AuthState[K] | ((draft: AuthState[K]) => AuthState[K]), options?: unknown) => boolean]);
|
|
10
|
+
export declare const login: (user: {
|
|
11
|
+
id: string;
|
|
12
|
+
email: string;
|
|
13
|
+
}, token: string) => void;
|
|
14
|
+
export declare const logout: () => void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { gstate } from '../../index'
|
|
2
|
+
|
|
3
|
+
export interface AuthState extends Record<string, unknown> {
|
|
4
|
+
user: { id: string, email: string } | null
|
|
5
|
+
token: string | null
|
|
6
|
+
isAuthenticated: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Secure Authentication Store
|
|
11
|
+
* RECOMMENDED FOR: Frontend (FE) / Backend (BE)
|
|
12
|
+
*
|
|
13
|
+
* Demonstrates the 'encoded' option for basic obfuscation in localStorage.
|
|
14
|
+
* Notice: For industrial-grade security, use encryptionKey in config.
|
|
15
|
+
*/
|
|
16
|
+
export const useAuth = gstate<AuthState>({
|
|
17
|
+
user: null,
|
|
18
|
+
token: null,
|
|
19
|
+
isAuthenticated: false
|
|
20
|
+
}, {
|
|
21
|
+
namespace: 'secure-auth',
|
|
22
|
+
encoded: true, // Base64 encodes data in localStorage
|
|
23
|
+
persist: true
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export const login = (user: { id: string, email: string }, token: string) => {
|
|
27
|
+
useAuth.transaction(() => {
|
|
28
|
+
useAuth.set('user', user)
|
|
29
|
+
useAuth.set('token', token)
|
|
30
|
+
useAuth.set('isAuthenticated', true)
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const logout = () => {
|
|
35
|
+
useAuth.deleteAll() // Clears all state and storage for this namespace
|
|
36
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface VaultState extends Record<string, unknown> {
|
|
2
|
+
encryptedData: string | null;
|
|
3
|
+
}
|
|
4
|
+
export declare const initSecureVault: () => Promise<import("../../advanced").IStore<VaultState> & (<K extends keyof VaultState>(key: K) => readonly [VaultState[K] | undefined, (val: VaultState[K] | ((draft: VaultState[K]) => VaultState[K]), options?: unknown) => boolean])>;
|
|
5
|
+
export declare const useProfileStore: import("../../advanced").IStore<{
|
|
6
|
+
bio: string;
|
|
7
|
+
displayName: string;
|
|
8
|
+
}> & (<K extends "bio" | "displayName">(key: K) => readonly [{
|
|
9
|
+
bio: string;
|
|
10
|
+
displayName: string;
|
|
11
|
+
}[K] | undefined, (val: {
|
|
12
|
+
bio: string;
|
|
13
|
+
displayName: string;
|
|
14
|
+
}[K] | ((draft: {
|
|
15
|
+
bio: string;
|
|
16
|
+
displayName: string;
|
|
17
|
+
}[K]) => {
|
|
18
|
+
bio: string;
|
|
19
|
+
displayName: string;
|
|
20
|
+
}[K]), options?: unknown) => boolean]);
|
|
21
|
+
export declare const purgeUserData: (store: any, userId: string) => void;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { gstate, setAuditLogger, generateEncryptionKey } from '../../index'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SECURITY BEST PRACTICES EXAMPLE
|
|
5
|
+
*
|
|
6
|
+
* RECOMMENDED FOR: Frontend (FE) and Backend (BE/Node)
|
|
7
|
+
*
|
|
8
|
+
* DESIGN PRINCIPLE:
|
|
9
|
+
* - NEVER hardcode sensitive data (PII, Credit Cards, Keys) in source code.
|
|
10
|
+
* - Use environment variables or secure user input at runtime.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface VaultState extends Record<string, unknown> {
|
|
14
|
+
encryptedData: string | null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 1. AES-256-GCM Encryption (Enterprise Grade)
|
|
19
|
+
* This store demonstrates how to handle sensitive information securely.
|
|
20
|
+
*/
|
|
21
|
+
export const initSecureVault = async () => {
|
|
22
|
+
// At runtime, you would typically fetch the key from a secure vault or env
|
|
23
|
+
const masterKey = await generateEncryptionKey()
|
|
24
|
+
|
|
25
|
+
return gstate<VaultState>({
|
|
26
|
+
encryptedData: null
|
|
27
|
+
}, {
|
|
28
|
+
namespace: 'secure-vault',
|
|
29
|
+
persist: true,
|
|
30
|
+
encryptionKey: masterKey, // Real AES-256-GCM encryption
|
|
31
|
+
encoded: true
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 2. Field-Level Sanitization (XSS Defense)
|
|
37
|
+
* RECOMMENDED FOR: Frontend (FE)
|
|
38
|
+
* Automatically strips <script> and other XSS vectors from input values.
|
|
39
|
+
*/
|
|
40
|
+
export const useProfileStore = gstate({
|
|
41
|
+
bio: '',
|
|
42
|
+
displayName: ''
|
|
43
|
+
}, {
|
|
44
|
+
validateInput: true // Enables automatic sanitizeValue() on all .set() calls
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 3. Audit Logging Integration
|
|
49
|
+
* RECOMMENDED FOR: Backend (BE) / Regulatory Compliance
|
|
50
|
+
* Tracks every 'set' or 'delete' operation for accountability.
|
|
51
|
+
*/
|
|
52
|
+
const auditBuffer: any[] = []
|
|
53
|
+
setAuditLogger((entry) => {
|
|
54
|
+
// In a real BE scenario, send this to a logging service (Datadog, Elastic, etc.)
|
|
55
|
+
auditBuffer.push({
|
|
56
|
+
...entry,
|
|
57
|
+
timestamp: new Date().toISOString()
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 4. GDPR "Right to be Forgotten"
|
|
63
|
+
* RECOMMENDED FOR: Compliance
|
|
64
|
+
*/
|
|
65
|
+
export const purgeUserData = (store: any, userId: string) => {
|
|
66
|
+
// Wipe state and storage
|
|
67
|
+
store.deleteAll()
|
|
68
|
+
|
|
69
|
+
// Enterprise: Wipe GDPR consents and audit traces
|
|
70
|
+
store.deleteUserData(userId)
|
|
71
|
+
|
|
72
|
+
console.info(`[Security] Data purge requested for ${userId}`)
|
|
73
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export declare const useRapidStore: import("../../advanced").IStore<{
|
|
2
|
+
updates: number;
|
|
3
|
+
lastBatchTime: number;
|
|
4
|
+
}> & (<K extends "updates" | "lastBatchTime">(key: K) => readonly [{
|
|
5
|
+
updates: number;
|
|
6
|
+
lastBatchTime: number;
|
|
7
|
+
}[K] | undefined, (val: {
|
|
8
|
+
updates: number;
|
|
9
|
+
lastBatchTime: number;
|
|
10
|
+
}[K] | ((draft: {
|
|
11
|
+
updates: number;
|
|
12
|
+
lastBatchTime: number;
|
|
13
|
+
}[K]) => {
|
|
14
|
+
updates: number;
|
|
15
|
+
lastBatchTime: number;
|
|
16
|
+
}[K]), options?: unknown) => boolean]);
|
|
17
|
+
export declare const runRapidFire: (iterations?: number) => number;
|
|
18
|
+
export declare const usePayloadStore: import("../../advanced").IStore<{
|
|
19
|
+
data: any;
|
|
20
|
+
}> & (<K extends "data">(key: K) => readonly [{
|
|
21
|
+
data: any;
|
|
22
|
+
}[K] | undefined, (val: {
|
|
23
|
+
data: any;
|
|
24
|
+
}[K] | ((draft: {
|
|
25
|
+
data: any;
|
|
26
|
+
}[K]) => {
|
|
27
|
+
data: any;
|
|
28
|
+
}[K]), options?: unknown) => boolean]);
|
|
29
|
+
export declare const generateBigPayload: (keys?: number) => number;
|
|
30
|
+
export declare const useChainStore: import("../../advanced").IStore<{
|
|
31
|
+
base: number;
|
|
32
|
+
}> & (<K extends "base">(key: K) => readonly [{
|
|
33
|
+
base: number;
|
|
34
|
+
}[K] | undefined, (val: {
|
|
35
|
+
base: number;
|
|
36
|
+
}[K] | ((draft: {
|
|
37
|
+
base: number;
|
|
38
|
+
}[K]) => {
|
|
39
|
+
base: number;
|
|
40
|
+
}[K]), options?: unknown) => boolean]);
|
|
41
|
+
export declare const setupChain: () => void;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { gstate } from '../../index'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Rapid Fire Stress Test
|
|
5
|
+
* Tests RGS ability to handle massive bursts of updates.
|
|
6
|
+
*/
|
|
7
|
+
export const useRapidStore = gstate({
|
|
8
|
+
updates: 0,
|
|
9
|
+
lastBatchTime: 0
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
export const runRapidFire = (iterations: number = 1000) => {
|
|
13
|
+
const start = performance.now()
|
|
14
|
+
for (let i = 0; i < iterations; i++) {
|
|
15
|
+
useRapidStore.set('updates', i + 1)
|
|
16
|
+
}
|
|
17
|
+
const end = performance.now()
|
|
18
|
+
useRapidStore.set('lastBatchTime', end - start)
|
|
19
|
+
return end - start
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Big Payload Stress Test
|
|
24
|
+
* Tests memory handling and cloning performance with large objects.
|
|
25
|
+
*/
|
|
26
|
+
export const usePayloadStore = gstate({
|
|
27
|
+
data: {} as any
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
export const generateBigPayload = (keys: number = 5000) => {
|
|
31
|
+
const bigObj: Record<string, any> = {}
|
|
32
|
+
for (let i = 0; i < keys; i++) {
|
|
33
|
+
bigObj[`key_${i}`] = {
|
|
34
|
+
id: i,
|
|
35
|
+
value: Math.random(),
|
|
36
|
+
meta: { timestamp: Date.now(), tags: ['test', 'stress', 'payload'] }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const start = performance.now()
|
|
41
|
+
usePayloadStore.set('data', bigObj)
|
|
42
|
+
const end = performance.now()
|
|
43
|
+
return end - start
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Computed Chain Stress Test
|
|
48
|
+
* Tests dependency tracking and recursive updates.
|
|
49
|
+
*/
|
|
50
|
+
export const useChainStore = gstate({
|
|
51
|
+
base: 0
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
// Create a chain of 5 computed values
|
|
55
|
+
export const setupChain = () => {
|
|
56
|
+
useChainStore.compute('level1', (get) => (get<number>('base') || 0) + 1)
|
|
57
|
+
useChainStore.compute('level2', (get) => (get<number>('level1') || 0) + 1)
|
|
58
|
+
useChainStore.compute('level3', (get) => (get<number>('level2') || 0) + 1)
|
|
59
|
+
useChainStore.compute('level4', (get) => (get<number>('level3') || 0) + 1)
|
|
60
|
+
useChainStore.compute('level5', (get) => (get<number>('level4') || 0) + 1)
|
|
61
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type IStore } from '../../index';
|
|
2
|
+
export interface EditorState extends Record<string, unknown> {
|
|
3
|
+
content: string;
|
|
4
|
+
cursorPosition: number;
|
|
5
|
+
}
|
|
6
|
+
export declare const useEditor: IStore<EditorState> & (<K extends keyof EditorState>(key: K) => readonly [EditorState[K] | undefined, (val: EditorState[K] | ((draft: EditorState[K]) => EditorState[K]), options?: unknown) => boolean]);
|
|
7
|
+
export declare const insertText: (text: string) => void;
|
|
8
|
+
export declare const undo: () => any;
|
|
9
|
+
export declare const redo: () => any;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { gstate, undoRedoPlugin, type IStore } from '../../index'
|
|
2
|
+
|
|
3
|
+
export interface EditorState extends Record<string, unknown> {
|
|
4
|
+
content: string
|
|
5
|
+
cursorPosition: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Editor Store with History
|
|
10
|
+
* RECOMMENDED FOR: Frontend (FE)
|
|
11
|
+
*/
|
|
12
|
+
export const useEditor = gstate<EditorState>({
|
|
13
|
+
content: '',
|
|
14
|
+
cursorPosition: 0
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
// Add Plugin with safe type casting for demonstration
|
|
18
|
+
useEditor._addPlugin(undoRedoPlugin({
|
|
19
|
+
limit: 50
|
|
20
|
+
}) as any)
|
|
21
|
+
|
|
22
|
+
export const insertText = (text: string) => {
|
|
23
|
+
const current = useEditor.get('content') || ''
|
|
24
|
+
useEditor.set('content', current + text)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Plugin methods access
|
|
28
|
+
export const undo = () => (useEditor.plugins as any).undoRedo?.undo()
|
|
29
|
+
export const redo = () => (useEditor.plugins as any).undoRedo?.redo()
|
package/index.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ import { createStore as baseCreateStore } from "./core/store";
|
|
|
2
2
|
import { useStore as baseUseStore } from "./core/hooks";
|
|
3
3
|
import * as Security from "./core/security";
|
|
4
4
|
import type { IStore, StoreConfig } from "./core/types";
|
|
5
|
-
export declare const gstate: <S extends Record<string, unknown>>(initialState: S, configOrNamespace?: string | StoreConfig<S>) => IStore<S> & ((key:
|
|
5
|
+
export declare const gstate: <S extends Record<string, unknown>>(initialState: S, configOrNamespace?: string | StoreConfig<S>) => IStore<S> & (<K extends keyof S>(key: K) => readonly [S[K] | undefined, (val: S[K] | ((draft: S[K]) => S[K]), options?: unknown) => boolean]);
|
|
6
6
|
export { baseCreateStore as createStore };
|
|
7
|
-
export { useStore, useIsStoreReady, initState, destroyState, useStore as useGState, useStore as useSimpleState } from "./core/hooks";
|
|
7
|
+
export { useStore, useIsStoreReady, initState, getStore, destroyState, useStore as useGState, useStore as useSimpleState } from "./core/hooks";
|
|
8
8
|
export { createAsyncStore } from "./core/async";
|
|
9
9
|
export * from "./plugins/index";
|
|
10
10
|
export { generateEncryptionKey, exportKey, importKey, isCryptoAvailable, setAuditLogger, logAudit, validateKey, sanitizeValue } from "./core/security";
|