@owlmeans/state 0.1.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/LICENSE +21 -0
- package/README.md +456 -0
- package/build/.gitkeep +0 -0
- package/build/consts.d.ts +3 -0
- package/build/consts.d.ts.map +1 -0
- package/build/consts.js +3 -0
- package/build/consts.js.map +1 -0
- package/build/errors.d.ts +10 -0
- package/build/errors.d.ts.map +1 -0
- package/build/errors.js +18 -0
- package/build/errors.js.map +1 -0
- package/build/index.d.ts +5 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +4 -0
- package/build/index.js.map +1 -0
- package/build/resource.d.ts +6 -0
- package/build/resource.d.ts.map +1 -0
- package/build/resource.js +208 -0
- package/build/resource.js.map +1 -0
- package/build/types.d.ts +39 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/build/utils/index.d.ts +2 -0
- package/build/utils/index.d.ts.map +1 -0
- package/build/utils/index.js +2 -0
- package/build/utils/index.js.map +1 -0
- package/build/utils/model.d.ts +4 -0
- package/build/utils/model.d.ts.map +1 -0
- package/build/utils/model.js +30 -0
- package/build/utils/model.js.map +1 -0
- package/package.json +34 -0
- package/src/consts.ts +3 -0
- package/src/errors.ts +23 -0
- package/src/index.ts +6 -0
- package/src/resource.ts +256 -0
- package/src/types.ts +49 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/model.ts +42 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsbuildinfo +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 OwlMeans Common — Fullstack typescript framework
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
# @owlmeans/state
|
|
2
|
+
|
|
3
|
+
A reactive state management library for OwlMeans Common applications. This package provides a comprehensive state management system with subscription-based reactivity, resource integration, and real-time data synchronization for building responsive fullstack applications.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `@owlmeans/state` package is a reactive state management library in the OwlMeans Common ecosystem that provides:
|
|
8
|
+
|
|
9
|
+
- **Reactive State Management**: Subscribe to state changes with automatic updates
|
|
10
|
+
- **Resource Integration**: Built on top of OwlMeans Resource system for data persistence
|
|
11
|
+
- **Real-time Synchronization**: Live updates across multiple subscribers
|
|
12
|
+
- **Model-Based Architecture**: Type-safe state models with built-in update methods
|
|
13
|
+
- **Query Subscriptions**: Subscribe to filtered data sets with criteria
|
|
14
|
+
- **Memory Management**: Automatic cleanup and unsubscription handling
|
|
15
|
+
- **Context Integration**: Seamless integration with OwlMeans context system
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @owlmeans/state
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Core Concepts
|
|
24
|
+
|
|
25
|
+
### State Resource
|
|
26
|
+
|
|
27
|
+
The `StateResource` extends the standard OwlMeans Resource interface with reactive capabilities, allowing components to subscribe to data changes and receive automatic updates.
|
|
28
|
+
|
|
29
|
+
### State Model
|
|
30
|
+
|
|
31
|
+
A `StateModel` wraps resource records with reactive capabilities, providing methods to update data and automatically notify subscribers of changes.
|
|
32
|
+
|
|
33
|
+
### Subscriptions
|
|
34
|
+
|
|
35
|
+
Subscriptions connect components to state changes, automatically triggering updates when subscribed data changes.
|
|
36
|
+
|
|
37
|
+
### Listeners
|
|
38
|
+
|
|
39
|
+
Functions that handle state change notifications, receiving updated state models when data changes.
|
|
40
|
+
|
|
41
|
+
## API Reference
|
|
42
|
+
|
|
43
|
+
### Types
|
|
44
|
+
|
|
45
|
+
#### `StateResource<T extends ResourceRecord>`
|
|
46
|
+
Extended resource interface with subscription capabilities.
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
interface StateResource<T extends ResourceRecord> extends Resource<T> {
|
|
50
|
+
subscribe: (params: StateSubscriptionOption<T>) => [() => void, StateModel<T>[]]
|
|
51
|
+
listen: (listener: StateListener<T>) => () => void
|
|
52
|
+
erase: () => Promise<void>
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
#### `StateModel<T extends ResourceRecord>`
|
|
57
|
+
Reactive wrapper for resource records.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
interface StateModel<T extends ResourceRecord> {
|
|
61
|
+
record: T
|
|
62
|
+
commit: (force?: boolean) => void
|
|
63
|
+
update: (data?: Partial<T>) => void
|
|
64
|
+
clear: () => void
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
#### `StateSubscriptionOption<T>`
|
|
69
|
+
Configuration for state subscriptions.
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
interface StateSubscriptionOption<T extends ResourceRecord> {
|
|
73
|
+
id?: string | string[] // Specific record IDs to subscribe to
|
|
74
|
+
_systemId?: string // System-level subscription identifier
|
|
75
|
+
query?: ListCriteria // Query criteria for filtered subscriptions
|
|
76
|
+
default?: Partial<T> // Default data for new records
|
|
77
|
+
listener: StateListener<T> // Callback function for updates
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### `StateListener<T>`
|
|
82
|
+
Function type for handling state changes.
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
interface StateListener<T extends ResourceRecord> {
|
|
86
|
+
(record: StateModel<T>[]): void | Promise<void>
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Factory Functions
|
|
91
|
+
|
|
92
|
+
#### `createStateResource<R>(alias?: string): StateResource<R>`
|
|
93
|
+
Creates a new state resource with reactive capabilities.
|
|
94
|
+
|
|
95
|
+
**Parameters:**
|
|
96
|
+
- `alias`: Optional resource alias (defaults to 'state')
|
|
97
|
+
|
|
98
|
+
**Returns:** StateResource instance with subscription support
|
|
99
|
+
|
|
100
|
+
#### `appendStateResource<C, T>(ctx: T, alias?: string): T & StateResourceAppend`
|
|
101
|
+
Appends a state resource to an existing context.
|
|
102
|
+
|
|
103
|
+
**Parameters:**
|
|
104
|
+
- `ctx`: OwlMeans context instance
|
|
105
|
+
- `alias`: Optional resource alias
|
|
106
|
+
|
|
107
|
+
**Returns:** Context with state resource capabilities
|
|
108
|
+
|
|
109
|
+
### Core Methods
|
|
110
|
+
|
|
111
|
+
#### `subscribe(params: StateSubscriptionOption<T>): [() => void, StateModel<T>[]]`
|
|
112
|
+
Subscribes to state changes for specific records or queries.
|
|
113
|
+
|
|
114
|
+
**Parameters:**
|
|
115
|
+
- `params`: Subscription configuration with listener and criteria
|
|
116
|
+
|
|
117
|
+
**Returns:** Tuple of [unsubscribe function, current state models]
|
|
118
|
+
|
|
119
|
+
#### `listen(listener: StateListener<T>): () => void`
|
|
120
|
+
Adds a global listener for all state changes.
|
|
121
|
+
|
|
122
|
+
**Parameters:**
|
|
123
|
+
- `listener`: Function to handle state changes
|
|
124
|
+
|
|
125
|
+
**Returns:** Unsubscribe function
|
|
126
|
+
|
|
127
|
+
#### `erase(): Promise<void>`
|
|
128
|
+
Clears all state data and notifies subscribers.
|
|
129
|
+
|
|
130
|
+
### State Model Methods
|
|
131
|
+
|
|
132
|
+
#### `commit(force?: boolean): void`
|
|
133
|
+
Persists model changes to the underlying resource.
|
|
134
|
+
|
|
135
|
+
#### `update(data?: Partial<T>): void`
|
|
136
|
+
Updates model data and notifies subscribers.
|
|
137
|
+
|
|
138
|
+
#### `clear(): void`
|
|
139
|
+
Clears model data while maintaining subscription.
|
|
140
|
+
|
|
141
|
+
### Error Types
|
|
142
|
+
|
|
143
|
+
#### `StateToolingError`
|
|
144
|
+
Base error for state management tooling issues.
|
|
145
|
+
|
|
146
|
+
#### `StateListenerError`
|
|
147
|
+
Error related to listener management and execution.
|
|
148
|
+
|
|
149
|
+
## Usage Examples
|
|
150
|
+
|
|
151
|
+
### Basic State Subscription
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { createStateResource } from '@owlmeans/state'
|
|
155
|
+
|
|
156
|
+
interface User extends ResourceRecord {
|
|
157
|
+
id?: string
|
|
158
|
+
name: string
|
|
159
|
+
email: string
|
|
160
|
+
status: 'active' | 'inactive'
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Create state resource
|
|
164
|
+
const userState = createStateResource<User>('users')
|
|
165
|
+
|
|
166
|
+
// Subscribe to a specific user
|
|
167
|
+
const [unsubscribe, models] = userState.subscribe({
|
|
168
|
+
id: 'user123',
|
|
169
|
+
listener: (models) => {
|
|
170
|
+
const user = models[0]
|
|
171
|
+
console.log('User updated:', user.record)
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
// Get current state immediately
|
|
176
|
+
if (models.length > 0) {
|
|
177
|
+
console.log('Current user:', models[0].record)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Update user data
|
|
181
|
+
models[0].update({ status: 'inactive' })
|
|
182
|
+
models[0].commit()
|
|
183
|
+
|
|
184
|
+
// Cleanup
|
|
185
|
+
unsubscribe()
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Multiple Record Subscription
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// Subscribe to multiple users
|
|
192
|
+
const [unsubscribe, models] = userState.subscribe({
|
|
193
|
+
id: ['user1', 'user2', 'user3'],
|
|
194
|
+
listener: (models) => {
|
|
195
|
+
console.log(`Received updates for ${models.length} users`)
|
|
196
|
+
models.forEach(model => {
|
|
197
|
+
console.log(`User ${model.record.id}: ${model.record.name}`)
|
|
198
|
+
})
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Query-Based Subscription
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
// Subscribe to active users
|
|
207
|
+
const [unsubscribe, models] = userState.subscribe({
|
|
208
|
+
query: {
|
|
209
|
+
status: 'active',
|
|
210
|
+
role: ['admin', 'moderator']
|
|
211
|
+
},
|
|
212
|
+
listener: (models) => {
|
|
213
|
+
console.log(`Active admin/moderator users: ${models.length}`)
|
|
214
|
+
}
|
|
215
|
+
})
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Global State Listening
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
// Listen to all state changes
|
|
222
|
+
const unsubscribe = userState.listen((models) => {
|
|
223
|
+
console.log('Global state change detected')
|
|
224
|
+
// Handle any user state changes
|
|
225
|
+
})
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### React Integration Example
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
import React, { useEffect, useState } from 'react'
|
|
232
|
+
|
|
233
|
+
const UserProfile: React.FC<{ userId: string }> = ({ userId }) => {
|
|
234
|
+
const [user, setUser] = useState<StateModel<User> | null>(null)
|
|
235
|
+
|
|
236
|
+
useEffect(() => {
|
|
237
|
+
const [unsubscribe, models] = userState.subscribe({
|
|
238
|
+
id: userId,
|
|
239
|
+
default: { name: '', email: '', status: 'active' },
|
|
240
|
+
listener: (models) => {
|
|
241
|
+
setUser(models[0] || null)
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
// Set initial state
|
|
246
|
+
if (models.length > 0) {
|
|
247
|
+
setUser(models[0])
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return unsubscribe
|
|
251
|
+
}, [userId])
|
|
252
|
+
|
|
253
|
+
const handleUpdate = (data: Partial<User>) => {
|
|
254
|
+
if (user) {
|
|
255
|
+
user.update(data)
|
|
256
|
+
user.commit()
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (!user) {
|
|
261
|
+
return <div>Loading...</div>
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return (
|
|
265
|
+
<div>
|
|
266
|
+
<h2>{user.record.name}</h2>
|
|
267
|
+
<p>Email: {user.record.email}</p>
|
|
268
|
+
<p>Status: {user.record.status}</p>
|
|
269
|
+
<button onClick={() => handleUpdate({ status: 'inactive' })}>
|
|
270
|
+
Deactivate User
|
|
271
|
+
</button>
|
|
272
|
+
</div>
|
|
273
|
+
)
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Context Integration
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
import { createBasicContext } from '@owlmeans/context'
|
|
281
|
+
import { appendStateResource } from '@owlmeans/state'
|
|
282
|
+
|
|
283
|
+
// Create context with state capabilities
|
|
284
|
+
const context = createBasicContext()
|
|
285
|
+
appendStateResource(context, 'user-state')
|
|
286
|
+
|
|
287
|
+
// Access state resource from context
|
|
288
|
+
const stateResource = context.getStateResource<User>('user-state')
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Bulk Operations
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
// Subscribe to system-level changes
|
|
295
|
+
const [unsubscribe, models] = userState.subscribe({
|
|
296
|
+
_systemId: 'bulk-operations',
|
|
297
|
+
listener: async (models) => {
|
|
298
|
+
// Handle bulk updates
|
|
299
|
+
console.log(`Processing ${models.length} records`)
|
|
300
|
+
|
|
301
|
+
// Batch commit changes
|
|
302
|
+
models.forEach(model => model.commit())
|
|
303
|
+
}
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
// Perform bulk updates
|
|
307
|
+
await userState.create({ name: 'User 1', email: 'user1@example.com' })
|
|
308
|
+
await userState.create({ name: 'User 2', email: 'user2@example.com' })
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Default Data Handling
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
// Subscribe with default data for new records
|
|
315
|
+
const [unsubscribe, models] = userState.subscribe({
|
|
316
|
+
id: 'new-user',
|
|
317
|
+
default: {
|
|
318
|
+
name: 'New User',
|
|
319
|
+
email: '',
|
|
320
|
+
status: 'active'
|
|
321
|
+
},
|
|
322
|
+
listener: (models) => {
|
|
323
|
+
const user = models[0]
|
|
324
|
+
if (!user.record.email) {
|
|
325
|
+
// User still needs email
|
|
326
|
+
console.log('User needs email address')
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
})
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Error Handling
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
import { StateListenerError } from '@owlmeans/state'
|
|
336
|
+
|
|
337
|
+
try {
|
|
338
|
+
const [unsubscribe, models] = userState.subscribe({
|
|
339
|
+
id: 'user123',
|
|
340
|
+
listener: async (models) => {
|
|
341
|
+
// Async listener that might throw
|
|
342
|
+
await processUserData(models[0].record)
|
|
343
|
+
}
|
|
344
|
+
})
|
|
345
|
+
} catch (error) {
|
|
346
|
+
if (error instanceof StateListenerError) {
|
|
347
|
+
console.error('Listener error:', error.message)
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## Advanced Features
|
|
353
|
+
|
|
354
|
+
### Custom Subscription Patterns
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
// Conditional subscriptions
|
|
358
|
+
const subscribeToUser = (userId: string, condition: (user: User) => boolean) => {
|
|
359
|
+
return userState.subscribe({
|
|
360
|
+
id: userId,
|
|
361
|
+
listener: (models) => {
|
|
362
|
+
const user = models[0]
|
|
363
|
+
if (user && condition(user.record)) {
|
|
364
|
+
// Handle conditional updates
|
|
365
|
+
console.log('Condition met for user:', user.record.id)
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
})
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Subscribe only to active users
|
|
372
|
+
const [unsubscribe] = subscribeToUser('user123', user => user.status === 'active')
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Memory Management
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
class UserManager {
|
|
379
|
+
private subscriptions: (() => void)[] = []
|
|
380
|
+
|
|
381
|
+
subscribeToUser(userId: string) {
|
|
382
|
+
const [unsubscribe] = userState.subscribe({
|
|
383
|
+
id: userId,
|
|
384
|
+
listener: this.handleUserUpdate.bind(this)
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
this.subscriptions.push(unsubscribe)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
cleanup() {
|
|
391
|
+
// Clean up all subscriptions
|
|
392
|
+
this.subscriptions.forEach(unsubscribe => unsubscribe())
|
|
393
|
+
this.subscriptions = []
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
private handleUserUpdate(models: StateModel<User>[]) {
|
|
397
|
+
// Handle updates
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Performance Optimization
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
// Debounced updates
|
|
406
|
+
let updateTimeout: NodeJS.Timeout
|
|
407
|
+
|
|
408
|
+
const [unsubscribe] = userState.subscribe({
|
|
409
|
+
id: 'user123',
|
|
410
|
+
listener: (models) => {
|
|
411
|
+
clearTimeout(updateTimeout)
|
|
412
|
+
updateTimeout = setTimeout(() => {
|
|
413
|
+
// Process updates after debounce
|
|
414
|
+
console.log('Processing debounced update')
|
|
415
|
+
}, 100)
|
|
416
|
+
}
|
|
417
|
+
})
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## Integration with OwlMeans Ecosystem
|
|
421
|
+
|
|
422
|
+
The `@owlmeans/state` package integrates with:
|
|
423
|
+
|
|
424
|
+
- **@owlmeans/resource**: Built on the resource system for data persistence
|
|
425
|
+
- **@owlmeans/context**: Context-based service registration and access
|
|
426
|
+
- **@owlmeans/client**: React hooks and component integration
|
|
427
|
+
- **@owlmeans/error**: Error handling and reporting
|
|
428
|
+
- **@owlmeans/client-resource**: Client-side resource management
|
|
429
|
+
|
|
430
|
+
## Best Practices
|
|
431
|
+
|
|
432
|
+
### Subscription Management
|
|
433
|
+
- Always store unsubscribe functions and call them during cleanup
|
|
434
|
+
- Use specific IDs when possible instead of broad queries
|
|
435
|
+
- Implement proper error handling in listeners
|
|
436
|
+
- Avoid creating subscriptions in render loops
|
|
437
|
+
|
|
438
|
+
### Performance
|
|
439
|
+
- Use query-based subscriptions for filtered data sets
|
|
440
|
+
- Implement debouncing for high-frequency updates
|
|
441
|
+
- Clean up unused subscriptions promptly
|
|
442
|
+
- Use default data to avoid loading states
|
|
443
|
+
|
|
444
|
+
### Memory Management
|
|
445
|
+
- Implement proper cleanup in component unmount
|
|
446
|
+
- Use weak references for large object graphs
|
|
447
|
+
- Monitor subscription count in development
|
|
448
|
+
- Clean up system subscriptions when no longer needed
|
|
449
|
+
|
|
450
|
+
### Error Handling
|
|
451
|
+
- Wrap async listeners in try-catch blocks
|
|
452
|
+
- Implement fallback UI for failed state updates
|
|
453
|
+
- Log subscription errors for debugging
|
|
454
|
+
- Use defensive programming for model access
|
|
455
|
+
|
|
456
|
+
Fixes #32.
|
package/build/.gitkeep
ADDED
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,UAAU,aAAa,CAAA;AACpC,eAAO,MAAM,aAAa,UAAU,CAAA"}
|
package/build/consts.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAA;AACpC,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ResourceError } from '@owlmeans/resource';
|
|
2
|
+
export declare class StateToolingError extends ResourceError {
|
|
3
|
+
static typeName: string;
|
|
4
|
+
constructor(msg: string);
|
|
5
|
+
}
|
|
6
|
+
export declare class StateListenerError extends StateToolingError {
|
|
7
|
+
static typeName: string;
|
|
8
|
+
constructor(msg: string);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAElD,qBAAa,iBAAkB,SAAQ,aAAa;IAClD,OAAuB,QAAQ,SAAqC;gBAExD,GAAG,EAAE,MAAM;CAIxB;AAED,qBAAa,kBAAmB,SAAQ,iBAAiB;IACvD,OAAuB,QAAQ,SAA0C;gBAE7D,GAAG,EAAE,MAAM;CAIxB"}
|
package/build/errors.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ResourceError } from '@owlmeans/resource';
|
|
2
|
+
export class StateToolingError extends ResourceError {
|
|
3
|
+
static typeName = `${ResourceError.typeName}Tooling`;
|
|
4
|
+
constructor(msg) {
|
|
5
|
+
super(`tooling:${msg}`);
|
|
6
|
+
this.type = StateToolingError.typeName;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export class StateListenerError extends StateToolingError {
|
|
10
|
+
static typeName = `${StateToolingError.typeName}Listener`;
|
|
11
|
+
constructor(msg) {
|
|
12
|
+
super(`listener:${msg}`);
|
|
13
|
+
this.type = StateListenerError.typeName;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
ResourceError.registerErrorClass(StateToolingError);
|
|
17
|
+
ResourceError.registerErrorClass(StateListenerError);
|
|
18
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAElD,MAAM,OAAO,iBAAkB,SAAQ,aAAa;IAC3C,MAAM,CAAU,QAAQ,GAAG,GAAG,aAAa,CAAC,QAAQ,SAAS,CAAA;IAEpE,YAAY,GAAW;QACrB,KAAK,CAAC,WAAW,GAAG,EAAE,CAAC,CAAA;QACvB,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC,QAAQ,CAAA;IACxC,CAAC;;AAGH,MAAM,OAAO,kBAAmB,SAAQ,iBAAiB;IAChD,MAAM,CAAU,QAAQ,GAAG,GAAG,iBAAiB,CAAC,QAAQ,UAAU,CAAA;IAEzE,YAAY,GAAW;QACrB,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC,CAAA;QACxB,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC,QAAQ,CAAA;IACzC,CAAC;;AAGH,aAAa,CAAC,kBAAkB,CAAC,iBAAiB,CAAC,CAAA;AACnD,aAAa,CAAC,kBAAkB,CAAC,kBAAkB,CAAC,CAAA"}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,mBAAmB,YAAY,CAAA;AAE/B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ResourceRecord } from '@owlmeans/resource';
|
|
2
|
+
import type { StateResource, StateResourceAppend } from './types.js';
|
|
3
|
+
import type { BasicContext as Context, BasicConfig as Config } from '@owlmeans/context';
|
|
4
|
+
export declare const createStateResource: <R extends ResourceRecord>(alias?: string) => StateResource<R>;
|
|
5
|
+
export declare const appendStateResource: <C extends Config, T extends Context<C>>(ctx: T, alias?: string) => T & StateResourceAppend;
|
|
6
|
+
//# sourceMappingURL=resource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAgC,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACtF,OAAO,KAAK,EAAiB,aAAa,EAAE,mBAAmB,EAA2B,MAAM,YAAY,CAAA;AAI5G,OAAO,KAAK,EAAE,YAAY,IAAI,OAAO,EAAE,WAAW,IAAI,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAEvF,eAAO,MAAM,mBAAmB,GAAI,CAAC,SAAS,cAAc,UAAS,MAAM,KAAmB,aAAa,CAAC,CAAC,CAuO5G,CAAA;AAED,eAAO,MAAM,mBAAmB,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,OACnE,CAAC,UAAS,MAAM,KACpB,CAAC,GAAG,mBAWN,CAAA"}
|