@owlmeans/static-resource 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 +330 -0
- package/build/.gitkeep +0 -0
- package/build/consts.d.ts +2 -0
- package/build/consts.d.ts.map +1 -0
- package/build/consts.js +2 -0
- package/build/consts.js.map +1 -0
- package/build/index.d.ts +4 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +3 -0
- package/build/index.js.map +1 -0
- package/build/resource.d.ts +5 -0
- package/build/resource.d.ts.map +1 -0
- package/build/resource.js +122 -0
- package/build/resource.js.map +1 -0
- package/build/types.d.ts +10 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/package.json +35 -0
- package/src/consts.ts +2 -0
- package/src/index.ts +4 -0
- package/src/resource.ts +160 -0
- package/src/types.ts +10 -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,330 @@
|
|
|
1
|
+
# @owlmeans/static-resource
|
|
2
|
+
|
|
3
|
+
The **@owlmeans/static-resource** package provides an in-memory resource storage solution for OwlMeans Common Libraries, designed for fullstack microservices and microclients development with focus on security and proper authentication and authorization.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
This package serves as a lightweight, in-memory resource implementation that:
|
|
8
|
+
|
|
9
|
+
- **Provides temporary storage** for resources that don't require persistent storage
|
|
10
|
+
- **Supports TTL (Time To Live)** for automatic cleanup of expired records
|
|
11
|
+
- **Integrates with context system** for dependency injection and resource management
|
|
12
|
+
- **Offers CRUD operations** with validation and error handling
|
|
13
|
+
- **Enables testing scenarios** where persistent storage is not needed
|
|
14
|
+
|
|
15
|
+
## Core Concepts
|
|
16
|
+
|
|
17
|
+
### Static Resource
|
|
18
|
+
A static resource is an in-memory storage that implements the standard `Resource<ResourceRecord>` interface from `@owlmeans/resource`. It stores records in memory and provides methods for creating, reading, and deleting records.
|
|
19
|
+
|
|
20
|
+
### Key-Value Storage
|
|
21
|
+
Records are stored using a key-value approach where each record must have an `id` field. Multiple static resource instances can share the same underlying storage by using the same key.
|
|
22
|
+
|
|
23
|
+
### TTL Support
|
|
24
|
+
Records can be created with a Time To Live (TTL) option, after which they are automatically removed from the storage.
|
|
25
|
+
|
|
26
|
+
## API Reference
|
|
27
|
+
|
|
28
|
+
### Factory Functions
|
|
29
|
+
|
|
30
|
+
#### `createStaticResource(alias?, key?): Resource<ResourceRecord>`
|
|
31
|
+
|
|
32
|
+
Creates a new static resource instance.
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { createStaticResource } from '@owlmeans/static-resource'
|
|
36
|
+
|
|
37
|
+
const resource = createStaticResource('my-cache')
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Parameters:**
|
|
41
|
+
- `alias`: string (optional) - Resource alias, defaults to `DEFAULT_ALIAS` ('static')
|
|
42
|
+
- `key`: string (optional) - Storage key, defaults to the alias value
|
|
43
|
+
|
|
44
|
+
**Returns:** Resource instance implementing full CRUD operations
|
|
45
|
+
|
|
46
|
+
#### `appendStaticResource<C, T>(ctx, alias?, key?): T & StaticResourceAppend`
|
|
47
|
+
|
|
48
|
+
Adds static resource functionality to a context.
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { appendStaticResource } from '@owlmeans/static-resource'
|
|
52
|
+
import { makeBasicContext, makeBasicConfig, AppType } from '@owlmeans/context'
|
|
53
|
+
|
|
54
|
+
const config = makeBasicConfig(AppType.Backend, 'my-service')
|
|
55
|
+
const context = makeBasicContext(config)
|
|
56
|
+
const contextWithStatic = appendStaticResource(context, 'cache')
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Parameters:**
|
|
60
|
+
- `ctx`: T - The context to extend
|
|
61
|
+
- `alias`: string (optional) - Resource alias, defaults to `DEFAULT_ALIAS`
|
|
62
|
+
- `key`: string (optional) - Storage key, defaults to the alias value
|
|
63
|
+
|
|
64
|
+
**Returns:** Extended context with `getStaticResource` method
|
|
65
|
+
|
|
66
|
+
### Resource Methods
|
|
67
|
+
|
|
68
|
+
The static resource implements the standard `Resource<ResourceRecord>` interface:
|
|
69
|
+
|
|
70
|
+
#### `get<T>(id, field?, opts?): Promise<T>`
|
|
71
|
+
|
|
72
|
+
Retrieves a record by ID or field value. Throws `UnknownRecordError` if not found.
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
const user = await resource.get<User>('user-123')
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### `load<T>(id, field?, opts?): Promise<T | null>`
|
|
79
|
+
|
|
80
|
+
Loads a record by ID or field value. Returns `null` if not found.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const user = await resource.load<User>('user-123')
|
|
84
|
+
if (user) {
|
|
85
|
+
console.log('User found:', user.name)
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### `list<T>(criteria?, opts?): Promise<ListResult<T>>`
|
|
90
|
+
|
|
91
|
+
Lists all records in the storage.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const result = await resource.list<User>()
|
|
95
|
+
console.log('Total users:', result.items.length)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Note:** Criteria and options parameters are not supported and will throw `UnsupportedArgumentError`.
|
|
99
|
+
|
|
100
|
+
#### `create<T>(record, opts?): Promise<T>`
|
|
101
|
+
|
|
102
|
+
Creates a new record in the storage.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
const user = await resource.create({
|
|
106
|
+
id: 'user-123',
|
|
107
|
+
name: 'John Doe',
|
|
108
|
+
email: 'john@example.com'
|
|
109
|
+
}, { ttl: 3600 }) // TTL in seconds
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Options:**
|
|
113
|
+
- `ttl`: number | Date - Time to live in seconds (number) or expiration date (Date)
|
|
114
|
+
|
|
115
|
+
**Throws:**
|
|
116
|
+
- `UnknownRecordError` - If record doesn't have an `id` field
|
|
117
|
+
- `RecordExists` - If record with the same ID already exists
|
|
118
|
+
|
|
119
|
+
#### `delete(id, opts?): Promise<T | null>`
|
|
120
|
+
|
|
121
|
+
Deletes a record and returns it, or `null` if not found.
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
const deletedUser = await resource.delete('user-123')
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### `pick<T>(id, opts?): Promise<T>`
|
|
128
|
+
|
|
129
|
+
Deletes a record and returns it. Throws `UnknownRecordError` if not found.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const pickedUser = await resource.pick('user-123')
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Unsupported Methods
|
|
136
|
+
|
|
137
|
+
The following methods throw `UnsupportedMethodError`:
|
|
138
|
+
- `save()` - Static resources don't support updating existing records
|
|
139
|
+
- `update()` - Static resources don't support partial updates
|
|
140
|
+
|
|
141
|
+
### Types
|
|
142
|
+
|
|
143
|
+
#### `StaticResourceAppend`
|
|
144
|
+
|
|
145
|
+
Interface added to contexts when using `appendStaticResource`.
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
interface StaticResourceAppend {
|
|
149
|
+
getStaticResource: <T extends ResourceRecord>(alias?: string) => Resource<T>
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
#### `Config` and `Context`
|
|
154
|
+
|
|
155
|
+
Type aliases for basic context configuration and context interfaces.
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
interface Config extends BasicConfig {}
|
|
159
|
+
interface Context<C extends Config = Config> extends BasicContext<C> {}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Constants
|
|
163
|
+
|
|
164
|
+
#### `DEFAULT_ALIAS`
|
|
165
|
+
|
|
166
|
+
Default alias for static resources.
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
const DEFAULT_ALIAS = 'static'
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Usage Examples
|
|
173
|
+
|
|
174
|
+
### Basic Usage
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { createStaticResource } from '@owlmeans/static-resource'
|
|
178
|
+
|
|
179
|
+
// Create a static resource
|
|
180
|
+
const cache = createStaticResource('user-cache')
|
|
181
|
+
|
|
182
|
+
// Create records
|
|
183
|
+
await cache.create({ id: 'user-1', name: 'Alice', role: 'admin' })
|
|
184
|
+
await cache.create({ id: 'user-2', name: 'Bob', role: 'user' })
|
|
185
|
+
|
|
186
|
+
// Retrieve records
|
|
187
|
+
const alice = await cache.get('user-1')
|
|
188
|
+
console.log('Found user:', alice.name)
|
|
189
|
+
|
|
190
|
+
// List all records
|
|
191
|
+
const { items } = await cache.list()
|
|
192
|
+
console.log('Total users:', items.length)
|
|
193
|
+
|
|
194
|
+
// Delete a record
|
|
195
|
+
const deleted = await cache.delete('user-1')
|
|
196
|
+
console.log('Deleted user:', deleted?.name)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Context Integration
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import { appendStaticResource } from '@owlmeans/static-resource'
|
|
203
|
+
import { makeBasicContext, makeBasicConfig, AppType } from '@owlmeans/context'
|
|
204
|
+
|
|
205
|
+
const config = makeBasicConfig(AppType.Backend, 'user-service')
|
|
206
|
+
const context = makeBasicContext(config)
|
|
207
|
+
|
|
208
|
+
// Add static resource to context
|
|
209
|
+
const enhancedContext = appendStaticResource(context, 'session-cache')
|
|
210
|
+
|
|
211
|
+
// Configure and initialize context
|
|
212
|
+
enhancedContext.configure()
|
|
213
|
+
await enhancedContext.init()
|
|
214
|
+
|
|
215
|
+
// Use the static resource
|
|
216
|
+
const sessionCache = enhancedContext.getStaticResource('session-cache')
|
|
217
|
+
await sessionCache.create({
|
|
218
|
+
id: 'session-123',
|
|
219
|
+
userId: 'user-456',
|
|
220
|
+
expires: new Date(Date.now() + 3600000)
|
|
221
|
+
})
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### TTL Usage
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
import { createStaticResource } from '@owlmeans/static-resource'
|
|
228
|
+
|
|
229
|
+
const cache = createStaticResource('temporary-cache')
|
|
230
|
+
|
|
231
|
+
// Create record with TTL in seconds
|
|
232
|
+
await cache.create({
|
|
233
|
+
id: 'temp-data',
|
|
234
|
+
value: 'This will expire'
|
|
235
|
+
}, { ttl: 60 }) // Expires in 60 seconds
|
|
236
|
+
|
|
237
|
+
// Create record with expiration date
|
|
238
|
+
const expirationDate = new Date(Date.now() + 30000) // 30 seconds from now
|
|
239
|
+
await cache.create({
|
|
240
|
+
id: 'temp-data-2',
|
|
241
|
+
value: 'This will also expire'
|
|
242
|
+
}, { ttl: expirationDate })
|
|
243
|
+
|
|
244
|
+
// Records will be automatically removed after TTL expires
|
|
245
|
+
setTimeout(async () => {
|
|
246
|
+
const record = await cache.load('temp-data')
|
|
247
|
+
console.log('Record exists:', record !== null) // false
|
|
248
|
+
}, 61000)
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Shared Storage
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
import { createStaticResource } from '@owlmeans/static-resource'
|
|
255
|
+
|
|
256
|
+
// Multiple resources sharing the same storage
|
|
257
|
+
const cacheA = createStaticResource('cache-a', 'shared-storage')
|
|
258
|
+
const cacheB = createStaticResource('cache-b', 'shared-storage')
|
|
259
|
+
|
|
260
|
+
// Record created in cacheA is accessible from cacheB
|
|
261
|
+
await cacheA.create({ id: 'shared-record', data: 'shared data' })
|
|
262
|
+
const record = await cacheB.load('shared-record')
|
|
263
|
+
console.log('Shared record:', record?.data) // 'shared data'
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Error Handling
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
import { createStaticResource } from '@owlmeans/static-resource'
|
|
270
|
+
import { UnknownRecordError, RecordExists } from '@owlmeans/resource'
|
|
271
|
+
|
|
272
|
+
const resource = createStaticResource('error-demo')
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
// This will throw UnknownRecordError
|
|
276
|
+
await resource.get('non-existent-id')
|
|
277
|
+
} catch (error) {
|
|
278
|
+
if (error instanceof UnknownRecordError) {
|
|
279
|
+
console.log('Record not found:', error.message)
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
await resource.create({ id: 'user-1', name: 'Alice' })
|
|
285
|
+
// This will throw RecordExists
|
|
286
|
+
await resource.create({ id: 'user-1', name: 'Bob' })
|
|
287
|
+
} catch (error) {
|
|
288
|
+
if (error instanceof RecordExists) {
|
|
289
|
+
console.log('Record already exists:', error.message)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Error Handling
|
|
295
|
+
|
|
296
|
+
The package uses standard error types from `@owlmeans/resource`:
|
|
297
|
+
|
|
298
|
+
- **`UnknownRecordError`** - Thrown when trying to access a non-existent record
|
|
299
|
+
- **`RecordExists`** - Thrown when trying to create a record with an existing ID
|
|
300
|
+
- **`MisshapedRecord`** - Thrown when record structure is invalid (missing ID)
|
|
301
|
+
- **`UnsupportedArgumentError`** - Thrown when unsupported arguments are provided
|
|
302
|
+
- **`UnsupportedMethodError`** - Thrown when calling unsupported methods
|
|
303
|
+
|
|
304
|
+
## Integration with OwlMeans Common
|
|
305
|
+
|
|
306
|
+
This package follows the OwlMeans Common library structure:
|
|
307
|
+
- **types**: TypeScript interfaces and type definitions
|
|
308
|
+
- **consts**: Static values and constants
|
|
309
|
+
- **resource**: Resource implementation and factory functions
|
|
310
|
+
|
|
311
|
+
The static resource integrates seamlessly with other OwlMeans packages:
|
|
312
|
+
- Uses `@owlmeans/context` for dependency injection
|
|
313
|
+
- Implements `@owlmeans/resource` interface for consistency
|
|
314
|
+
- Supports `@owlmeans/error` error types
|
|
315
|
+
|
|
316
|
+
## Best Practices
|
|
317
|
+
|
|
318
|
+
1. **Use for temporary data** - Static resources are perfect for caching, sessions, or test data
|
|
319
|
+
2. **Implement TTL for cleanup** - Always set TTL for records that should expire automatically
|
|
320
|
+
3. **Share storage carefully** - Use the same key only when you want resources to share data
|
|
321
|
+
4. **Handle errors gracefully** - Always catch and handle resource-specific errors
|
|
322
|
+
5. **Integrate with context** - Use `appendStaticResource` for proper context integration
|
|
323
|
+
6. **Test scenarios** - Perfect for unit tests where you don't want persistent storage
|
|
324
|
+
|
|
325
|
+
## Dependencies
|
|
326
|
+
|
|
327
|
+
This package depends on:
|
|
328
|
+
- `@owlmeans/context` - For contextual resource management
|
|
329
|
+
- `@owlmeans/resource` - For resource interface and error types
|
|
330
|
+
- `@owlmeans/error` - For error handling infrastructure
|
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,aAAa,WAAW,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,aAAa,GAAG,QAAQ,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;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Resource, ResourceRecord } from '@owlmeans/resource';
|
|
2
|
+
import type { Config, Context, StaticResourceAppend } from './types.js';
|
|
3
|
+
export declare const createStaticResource: (alias?: string, key?: string) => Resource<ResourceRecord>;
|
|
4
|
+
export declare const appendStaticResource: <C extends Config, T extends Context<C>>(ctx: T, alias?: string, key?: string) => T & StaticResourceAppend;
|
|
5
|
+
//# 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,EAC8D,QAAQ,EAChF,cAAc,EACf,MAAM,oBAAoB,CAAA;AAE3B,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAMvE,eAAO,MAAM,oBAAoB,WAAW,MAAM,QAAwB,MAAM,6BAmI/E,CAAA;AAED,eAAO,MAAM,oBAAoB,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,OACpE,CAAC,UAAS,MAAM,QAAwB,MAAM,KAClD,CAAC,GAAG,oBAWN,CAAA"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { appendContextual } from '@owlmeans/context';
|
|
2
|
+
import { DEFAULT_ALIAS } from './consts.js';
|
|
3
|
+
import { MisshapedRecord, RecordExists, UnknownRecordError, UnsupportedArgumentError, UnsupportedMethodError } from '@owlmeans/resource';
|
|
4
|
+
const stores = {};
|
|
5
|
+
export const createStaticResource = (alias = DEFAULT_ALIAS, key) => {
|
|
6
|
+
key = key ?? alias;
|
|
7
|
+
const getStore = () => {
|
|
8
|
+
if (stores[key] == null) {
|
|
9
|
+
stores[key] = new Map();
|
|
10
|
+
}
|
|
11
|
+
return stores[key];
|
|
12
|
+
};
|
|
13
|
+
const resource = appendContextual(alias, {
|
|
14
|
+
get: async (id, field, opts) => {
|
|
15
|
+
const record = await resource.load(id, field, opts);
|
|
16
|
+
if (record == null) {
|
|
17
|
+
throw new UnknownRecordError(id);
|
|
18
|
+
}
|
|
19
|
+
return record;
|
|
20
|
+
},
|
|
21
|
+
load: async (id, field, opts) => {
|
|
22
|
+
field = field ?? 'id';
|
|
23
|
+
if (opts != null) {
|
|
24
|
+
throw new UnsupportedArgumentError('static:get:opts');
|
|
25
|
+
}
|
|
26
|
+
const store = getStore();
|
|
27
|
+
const record = field === 'id'
|
|
28
|
+
? store.get(id)
|
|
29
|
+
: [...store.values()].find(record => record[field] === id);
|
|
30
|
+
if (record == null) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
return record;
|
|
34
|
+
},
|
|
35
|
+
list: async (criteria, opts) => {
|
|
36
|
+
if (criteria != null) {
|
|
37
|
+
throw new UnsupportedArgumentError('static:list:criteria');
|
|
38
|
+
}
|
|
39
|
+
if (opts != null) {
|
|
40
|
+
throw new UnsupportedArgumentError('static:list:opts');
|
|
41
|
+
}
|
|
42
|
+
const result = {
|
|
43
|
+
items: [...getStore().values()]
|
|
44
|
+
};
|
|
45
|
+
return result;
|
|
46
|
+
},
|
|
47
|
+
save: () => { throw new UnsupportedMethodError('static:save'); },
|
|
48
|
+
create: async (record, opts) => {
|
|
49
|
+
const store = getStore();
|
|
50
|
+
if (record.id == null) {
|
|
51
|
+
throw new UnknownRecordError('id');
|
|
52
|
+
}
|
|
53
|
+
if (store.has(record.id)) {
|
|
54
|
+
throw new RecordExists(record.id);
|
|
55
|
+
}
|
|
56
|
+
store.set(record.id, record);
|
|
57
|
+
if (opts?.ttl != null) {
|
|
58
|
+
const ttl = opts.ttl instanceof Date
|
|
59
|
+
? (opts.ttl.getTime() - Date.now()) / 1000
|
|
60
|
+
: typeof opts.ttl === 'number'
|
|
61
|
+
? opts.ttl * 1000
|
|
62
|
+
: null;
|
|
63
|
+
if (ttl == null) {
|
|
64
|
+
throw new UnsupportedArgumentError('static:opts:ttl-string');
|
|
65
|
+
}
|
|
66
|
+
setTimeout(() => record.id != null && void store.delete(record.id), ttl);
|
|
67
|
+
}
|
|
68
|
+
return record;
|
|
69
|
+
},
|
|
70
|
+
update: () => { throw new UnsupportedMethodError('static:update'); },
|
|
71
|
+
delete: async (id, opts) => {
|
|
72
|
+
let record = null;
|
|
73
|
+
if (typeof id === 'object') {
|
|
74
|
+
if (id.id == null) {
|
|
75
|
+
throw new MisshapedRecord('id');
|
|
76
|
+
}
|
|
77
|
+
record = await resource.load(id.id);
|
|
78
|
+
}
|
|
79
|
+
else if (typeof opts === 'string') {
|
|
80
|
+
record = await resource.load(id, opts);
|
|
81
|
+
}
|
|
82
|
+
if (record == null) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
let field = 'id';
|
|
86
|
+
if (typeof opts === 'string') {
|
|
87
|
+
field = opts;
|
|
88
|
+
opts = undefined;
|
|
89
|
+
}
|
|
90
|
+
else if (typeof opts === 'object') {
|
|
91
|
+
field = opts.field ?? field;
|
|
92
|
+
}
|
|
93
|
+
id = record.id ?? '';
|
|
94
|
+
if (id === '') {
|
|
95
|
+
throw new MisshapedRecord('id');
|
|
96
|
+
}
|
|
97
|
+
const store = getStore();
|
|
98
|
+
if (store.delete(id)) {
|
|
99
|
+
return record;
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
},
|
|
103
|
+
pick: async (id, opts) => {
|
|
104
|
+
const record = await resource.delete(id, opts);
|
|
105
|
+
if (record == null) {
|
|
106
|
+
throw new UnknownRecordError((typeof id === 'string' ? id : id.id) ?? 'unknown');
|
|
107
|
+
}
|
|
108
|
+
return record;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
return resource;
|
|
112
|
+
};
|
|
113
|
+
export const appendStaticResource = (ctx, alias = DEFAULT_ALIAS, key) => {
|
|
114
|
+
const resource = createStaticResource(alias, key);
|
|
115
|
+
const _ctx = ctx;
|
|
116
|
+
_ctx.registerResource(resource);
|
|
117
|
+
if (_ctx.getStaticResource == null) {
|
|
118
|
+
_ctx.getStaticResource = alias => ctx.resource(alias ?? resource.alias);
|
|
119
|
+
}
|
|
120
|
+
return _ctx;
|
|
121
|
+
};
|
|
122
|
+
//# sourceMappingURL=resource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.js","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"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"}
|
package/build/types.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Resource, ResourceRecord } from '@owlmeans/resource';
|
|
2
|
+
import type { BasicConfig, BasicContext } from '@owlmeans/context';
|
|
3
|
+
export interface StaticResourceAppend {
|
|
4
|
+
getStaticResource: <T extends ResourceRecord>(alias?: string) => Resource<T>;
|
|
5
|
+
}
|
|
6
|
+
export interface Config extends BasicConfig {
|
|
7
|
+
}
|
|
8
|
+
export interface Context<C extends Config = Config> extends BasicContext<C> {
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAElE,MAAM,WAAW,oBAAoB;IACnC,iBAAiB,EAAE,CAAC,CAAC,SAAS,cAAc,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAA;CAC7E;AAED,MAAM,WAAW,MAAO,SAAQ,WAAW;CAAG;AAE9C,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,YAAY,CAAC,CAAC,CAAC;CAAG"}
|
package/build/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@owlmeans/static-resource",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "tsc -b",
|
|
7
|
+
"dev": "sleep 348 && nodemon -e ts,tsx,json --watch src --exec \"tsc -p ./tsconfig.json\"",
|
|
8
|
+
"watch": "tsc -b -w --preserveWatchOutput --pretty"
|
|
9
|
+
},
|
|
10
|
+
"main": "build/index.js",
|
|
11
|
+
"module": "build/index.js",
|
|
12
|
+
"types": "build/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": "./build/index.js",
|
|
16
|
+
"require": "./build/index.js",
|
|
17
|
+
"default": "./build/index.js",
|
|
18
|
+
"module": "./build/index.js",
|
|
19
|
+
"types": "./build/index.d.ts"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@owlmeans/context": "^0.1.0",
|
|
24
|
+
"@owlmeans/error": "^0.1.0",
|
|
25
|
+
"@owlmeans/resource": "^0.1.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"nodemon": "^3.1.7",
|
|
29
|
+
"typescript": "^5.6.3"
|
|
30
|
+
},
|
|
31
|
+
"private": false,
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/consts.ts
ADDED
package/src/index.ts
ADDED
package/src/resource.ts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { appendContextual } from '@owlmeans/context'
|
|
2
|
+
import { DEFAULT_ALIAS } from './consts.js'
|
|
3
|
+
import type {
|
|
4
|
+
GetterOptions, ListOptions, ListResult, LifecycleOptions, ListCriteria, Resource,
|
|
5
|
+
ResourceRecord
|
|
6
|
+
} from '@owlmeans/resource'
|
|
7
|
+
import { MisshapedRecord, RecordExists, UnknownRecordError, UnsupportedArgumentError, UnsupportedMethodError } from '@owlmeans/resource'
|
|
8
|
+
import type { Config, Context, StaticResourceAppend } from './types.js'
|
|
9
|
+
|
|
10
|
+
type Getter = string | GetterOptions
|
|
11
|
+
type ResourceType = Resource<ResourceRecord>
|
|
12
|
+
|
|
13
|
+
const stores: Record<string, Map<string, ResourceRecord>> = {}
|
|
14
|
+
export const createStaticResource = (alias: string = DEFAULT_ALIAS, key?: string) => {
|
|
15
|
+
key = key ?? alias
|
|
16
|
+
|
|
17
|
+
const getStore = () => {
|
|
18
|
+
if (stores[key] == null) {
|
|
19
|
+
stores[key] = new Map()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return stores[key]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const resource: Resource<ResourceRecord> = appendContextual<ResourceType>(alias, {
|
|
26
|
+
get: async <T extends ResourceRecord>(id: string, field?: Getter, opts?: LifecycleOptions) => {
|
|
27
|
+
const record = await resource.load<T>(id, field, opts)
|
|
28
|
+
if (record == null) {
|
|
29
|
+
throw new UnknownRecordError(id)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return record
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
load: async <T extends ResourceRecord>(id: string, field?: Getter, opts?: LifecycleOptions) => {
|
|
36
|
+
field = field ?? 'id'
|
|
37
|
+
if (opts != null) {
|
|
38
|
+
throw new UnsupportedArgumentError('static:get:opts')
|
|
39
|
+
}
|
|
40
|
+
const store = getStore()
|
|
41
|
+
const record = field === 'id'
|
|
42
|
+
? store.get(id) as T | null
|
|
43
|
+
: [...store.values()].find(
|
|
44
|
+
record => record[field as keyof typeof record] === id
|
|
45
|
+
) as T | undefined
|
|
46
|
+
|
|
47
|
+
if (record == null) {
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return record
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
list: async <T extends ResourceRecord>(criteria?: ListOptions | ListCriteria, opts?: ListOptions) => {
|
|
55
|
+
if (criteria != null) {
|
|
56
|
+
throw new UnsupportedArgumentError('static:list:criteria')
|
|
57
|
+
}
|
|
58
|
+
if (opts != null) {
|
|
59
|
+
throw new UnsupportedArgumentError('static:list:opts')
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const result: ListResult<T> = {
|
|
63
|
+
items: [...getStore().values()] as T[]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return result
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
save: () => { throw new UnsupportedMethodError('static:save') },
|
|
70
|
+
|
|
71
|
+
create: async (record, opts) => {
|
|
72
|
+
const store = getStore()
|
|
73
|
+
if (record.id == null) {
|
|
74
|
+
throw new UnknownRecordError('id')
|
|
75
|
+
}
|
|
76
|
+
if (store.has(record.id)) {
|
|
77
|
+
throw new RecordExists(record.id)
|
|
78
|
+
}
|
|
79
|
+
store.set(record.id, record)
|
|
80
|
+
if (opts?.ttl != null) {
|
|
81
|
+
const ttl = opts.ttl instanceof Date
|
|
82
|
+
? (opts.ttl.getTime() - Date.now()) / 1000
|
|
83
|
+
: typeof opts.ttl === 'number'
|
|
84
|
+
? opts.ttl * 1000
|
|
85
|
+
: null
|
|
86
|
+
if (ttl == null) {
|
|
87
|
+
throw new UnsupportedArgumentError('static:opts:ttl-string')
|
|
88
|
+
}
|
|
89
|
+
setTimeout(() => record.id != null && void store.delete(record.id), ttl)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return record as any
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
update: () => { throw new UnsupportedMethodError('static:update') },
|
|
96
|
+
|
|
97
|
+
delete: async (id, opts) => {
|
|
98
|
+
let record: ResourceRecord | null = null
|
|
99
|
+
if (typeof id === 'object') {
|
|
100
|
+
if (id.id == null) {
|
|
101
|
+
throw new MisshapedRecord('id')
|
|
102
|
+
}
|
|
103
|
+
record = await resource.load(id.id)
|
|
104
|
+
} else if (typeof opts === 'string') {
|
|
105
|
+
record = await resource.load(id, opts)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (record == null) {
|
|
109
|
+
return null
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let field = 'id'
|
|
113
|
+
if (typeof opts === 'string') {
|
|
114
|
+
field = opts
|
|
115
|
+
opts = undefined
|
|
116
|
+
} else if (typeof opts === 'object') {
|
|
117
|
+
field = opts.field ?? field
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
id = record.id ?? ''
|
|
121
|
+
|
|
122
|
+
if (id === '') {
|
|
123
|
+
throw new MisshapedRecord('id')
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const store = getStore()
|
|
127
|
+
if (store.delete(id)) {
|
|
128
|
+
return record as any
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return null
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
pick: async (id, opts) => {
|
|
135
|
+
const record = await resource.delete(id, opts)
|
|
136
|
+
if (record == null) {
|
|
137
|
+
throw new UnknownRecordError((typeof id === 'string' ? id : id.id) ?? 'unknown')
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return record as any
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
return resource
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export const appendStaticResource = <C extends Config, T extends Context<C>>(
|
|
148
|
+
ctx: T, alias: string = DEFAULT_ALIAS, key?: string
|
|
149
|
+
): T & StaticResourceAppend => {
|
|
150
|
+
const resource = createStaticResource(alias, key)
|
|
151
|
+
|
|
152
|
+
const _ctx = ctx as T & StaticResourceAppend
|
|
153
|
+
|
|
154
|
+
_ctx.registerResource(resource)
|
|
155
|
+
if (_ctx.getStaticResource == null) {
|
|
156
|
+
_ctx.getStaticResource = alias => ctx.resource(alias ?? resource.alias)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return _ctx
|
|
160
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Resource, ResourceRecord } from '@owlmeans/resource'
|
|
2
|
+
import type { BasicConfig, BasicContext } from '@owlmeans/context'
|
|
3
|
+
|
|
4
|
+
export interface StaticResourceAppend {
|
|
5
|
+
getStaticResource: <T extends ResourceRecord>(alias?: string) => Resource<T>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface Config extends BasicConfig {}
|
|
9
|
+
|
|
10
|
+
export interface Context<C extends Config = Config> extends BasicContext<C> {}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": [
|
|
3
|
+
"../tsconfig.default.json",
|
|
4
|
+
],
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"rootDir": "./src/", /* Specify the root folder within your source files. */
|
|
7
|
+
"outDir": "./build/", /* Specify an output folder for all emitted files. */
|
|
8
|
+
},
|
|
9
|
+
"exclude": [
|
|
10
|
+
"./dist/**/*",
|
|
11
|
+
"./build/**/*",
|
|
12
|
+
"./*.ts"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["./src/consts.ts","./src/index.ts","./src/resource.ts","./src/types.ts"],"version":"5.6.3"}
|