@flareone/kv 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 +46 -0
- package/dist/index.d.ts +241 -0
- package/dist/index.js +409 -0
- package/dist/index.js.map +1 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Flareone Contributors
|
|
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,46 @@
|
|
|
1
|
+
# @flareone/kv
|
|
2
|
+
|
|
3
|
+
Workers KV integration for Flareone framework.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @flareone/kv
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Type-safe Wrapper**: High-level API for `KVNamespace`
|
|
14
|
+
- **Caching**: Built-in TTL and caching strategies
|
|
15
|
+
- **Repository Pattern**: `KVRepository` base class for entity management
|
|
16
|
+
- **Batch Operations**: Efficient `setMany`, `deleteMany`, `list`
|
|
17
|
+
- **Metadata Support**: Simple API for storing/retrieving metadata
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { Module, Injectable, Inject } from '@flareone/core';
|
|
23
|
+
import { KVModule, KVService } from '@flareone/kv';
|
|
24
|
+
|
|
25
|
+
@Injectable()
|
|
26
|
+
class CacheService {
|
|
27
|
+
constructor(@Inject(KVService) private kv: KVService) {}
|
|
28
|
+
|
|
29
|
+
async saveUser(id: string, data: any) {
|
|
30
|
+
// Automatic JSON serialization + TTL
|
|
31
|
+
await this.kv.set(`user:${id}`, data, { expirationTtl: 3600 });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@Module({
|
|
36
|
+
imports: [
|
|
37
|
+
KVModule.forRoot({
|
|
38
|
+
binding: 'MY_KV', // Binding name in wrangler.toml
|
|
39
|
+
keyPrefix: 'app:'
|
|
40
|
+
})
|
|
41
|
+
]
|
|
42
|
+
})
|
|
43
|
+
class AppModule {}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
See [main repository](https://github.com/flareonejs/flareone) for full documentation.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { Type, DynamicModule, InjectionToken } from '@flareone/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @flareone/kv - Workers KV Integration
|
|
5
|
+
* High-level, type-safe wrapper for Cloudflare Workers KV
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
interface KVGetOptions {
|
|
9
|
+
type?: 'text' | 'json' | 'arrayBuffer' | 'stream';
|
|
10
|
+
cacheTtl?: number;
|
|
11
|
+
}
|
|
12
|
+
interface KVPutOptions {
|
|
13
|
+
expiration?: number;
|
|
14
|
+
expirationTtl?: number;
|
|
15
|
+
metadata?: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
interface KVListOptions {
|
|
18
|
+
prefix?: string;
|
|
19
|
+
limit?: number;
|
|
20
|
+
cursor?: string;
|
|
21
|
+
}
|
|
22
|
+
interface KVListResult<T = unknown> {
|
|
23
|
+
keys: Array<{
|
|
24
|
+
name: string;
|
|
25
|
+
expiration?: number;
|
|
26
|
+
metadata?: T;
|
|
27
|
+
}>;
|
|
28
|
+
list_complete: boolean;
|
|
29
|
+
cursor?: string;
|
|
30
|
+
}
|
|
31
|
+
interface KVValueWithMetadata<T, M = unknown> {
|
|
32
|
+
value: T | null;
|
|
33
|
+
metadata: M | null;
|
|
34
|
+
}
|
|
35
|
+
interface KVModuleOptions {
|
|
36
|
+
binding: string;
|
|
37
|
+
defaultTtl?: number;
|
|
38
|
+
keyPrefix?: string;
|
|
39
|
+
enableCache?: boolean;
|
|
40
|
+
cacheTtl?: number;
|
|
41
|
+
}
|
|
42
|
+
interface KVModuleAsyncOptions {
|
|
43
|
+
imports?: Array<Type | DynamicModule>;
|
|
44
|
+
useFactory: (...args: unknown[]) => KVModuleOptions | Promise<KVModuleOptions>;
|
|
45
|
+
inject?: InjectionToken[];
|
|
46
|
+
}
|
|
47
|
+
declare const KV_OPTIONS: InjectionToken<KVModuleOptions>;
|
|
48
|
+
declare const KV_NAMESPACE: InjectionToken<KVNamespace<string>>;
|
|
49
|
+
/**
|
|
50
|
+
* High-level KV service with type-safe operations
|
|
51
|
+
*/
|
|
52
|
+
declare class KVService {
|
|
53
|
+
private namespace;
|
|
54
|
+
private options;
|
|
55
|
+
/**
|
|
56
|
+
* Initialize with KV namespace and options
|
|
57
|
+
*/
|
|
58
|
+
initialize(namespace: KVNamespace, options: KVModuleOptions): void;
|
|
59
|
+
/**
|
|
60
|
+
* Get the underlying KV namespace
|
|
61
|
+
*/
|
|
62
|
+
getNamespace(): KVNamespace;
|
|
63
|
+
/**
|
|
64
|
+
* Build the full key with optional prefix
|
|
65
|
+
*/
|
|
66
|
+
private buildKey;
|
|
67
|
+
/**
|
|
68
|
+
* Get a value by key
|
|
69
|
+
*/
|
|
70
|
+
get<T = string>(key: string, options?: KVGetOptions): Promise<T | null>;
|
|
71
|
+
/**
|
|
72
|
+
* Get a value as text
|
|
73
|
+
*/
|
|
74
|
+
getText(key: string, cacheTtl?: number): Promise<string | null>;
|
|
75
|
+
/**
|
|
76
|
+
* Get a value as JSON
|
|
77
|
+
*/
|
|
78
|
+
getJson<T>(key: string, cacheTtl?: number): Promise<T | null>;
|
|
79
|
+
/**
|
|
80
|
+
* Get a value as ArrayBuffer
|
|
81
|
+
*/
|
|
82
|
+
getArrayBuffer(key: string, cacheTtl?: number): Promise<ArrayBuffer | null>;
|
|
83
|
+
/**
|
|
84
|
+
* Get a value as ReadableStream
|
|
85
|
+
*/
|
|
86
|
+
getStream(key: string, cacheTtl?: number): Promise<ReadableStream | null>;
|
|
87
|
+
/**
|
|
88
|
+
* Get a value with its metadata
|
|
89
|
+
*/
|
|
90
|
+
getWithMetadata<T = unknown, M = unknown>(key: string, type?: 'text' | 'json' | 'arrayBuffer' | 'stream'): Promise<KVValueWithMetadata<T, M>>;
|
|
91
|
+
/**
|
|
92
|
+
* Set a value
|
|
93
|
+
*/
|
|
94
|
+
set<T = unknown>(key: string, value: T, options?: KVPutOptions): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Set multiple values at once
|
|
97
|
+
*/
|
|
98
|
+
setMany<T = unknown>(entries: Array<{
|
|
99
|
+
key: string;
|
|
100
|
+
value: T;
|
|
101
|
+
options?: KVPutOptions;
|
|
102
|
+
}>): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Set a value with automatic JSON serialization
|
|
105
|
+
*/
|
|
106
|
+
setJson<T>(key: string, value: T, options?: KVPutOptions): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Set a value as raw text
|
|
109
|
+
*/
|
|
110
|
+
setText(key: string, value: string, options?: KVPutOptions): Promise<void>;
|
|
111
|
+
/**
|
|
112
|
+
* Set a value as ArrayBuffer
|
|
113
|
+
*/
|
|
114
|
+
setArrayBuffer(key: string, value: ArrayBuffer, options?: KVPutOptions): Promise<void>;
|
|
115
|
+
/**
|
|
116
|
+
* Delete a key
|
|
117
|
+
*/
|
|
118
|
+
delete(key: string): Promise<void>;
|
|
119
|
+
/**
|
|
120
|
+
* Delete multiple keys
|
|
121
|
+
*/
|
|
122
|
+
deleteMany(keys: string[]): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Delete all keys with a prefix
|
|
125
|
+
*/
|
|
126
|
+
deleteByPrefix(prefix: string): Promise<number>;
|
|
127
|
+
/**
|
|
128
|
+
* List keys
|
|
129
|
+
*/
|
|
130
|
+
list<M = unknown>(options?: KVListOptions): Promise<KVListResult<M>>;
|
|
131
|
+
/**
|
|
132
|
+
* Get all keys (handles pagination automatically)
|
|
133
|
+
*/
|
|
134
|
+
keys<M = unknown>(prefix?: string): Promise<Array<{
|
|
135
|
+
name: string;
|
|
136
|
+
metadata?: M;
|
|
137
|
+
}>>;
|
|
138
|
+
/**
|
|
139
|
+
* Check if a key exists
|
|
140
|
+
*/
|
|
141
|
+
exists(key: string): Promise<boolean>;
|
|
142
|
+
/**
|
|
143
|
+
* Get or set a value (cache pattern)
|
|
144
|
+
*/
|
|
145
|
+
getOrSet<T>(key: string, factory: () => T | Promise<T>, options?: KVPutOptions): Promise<T>;
|
|
146
|
+
/**
|
|
147
|
+
* Increment a numeric value
|
|
148
|
+
*/
|
|
149
|
+
increment(key: string, delta?: number): Promise<number>;
|
|
150
|
+
/**
|
|
151
|
+
* Decrement a numeric value
|
|
152
|
+
*/
|
|
153
|
+
decrement(key: string, delta?: number): Promise<number>;
|
|
154
|
+
/**
|
|
155
|
+
* Append to an array stored in KV
|
|
156
|
+
*/
|
|
157
|
+
append<T>(key: string, item: T, options?: KVPutOptions): Promise<T[]>;
|
|
158
|
+
/**
|
|
159
|
+
* Update a value with a transformer function
|
|
160
|
+
*/
|
|
161
|
+
update<T>(key: string, updater: (current: T | null) => T, options?: KVPutOptions): Promise<T>;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Base class for creating typed KV repositories
|
|
165
|
+
*/
|
|
166
|
+
declare abstract class KVRepository<T, M = unknown> {
|
|
167
|
+
protected readonly kv: KVService;
|
|
168
|
+
protected readonly prefix: string;
|
|
169
|
+
constructor(kv: KVService, prefix: string);
|
|
170
|
+
/**
|
|
171
|
+
* Build the full key for an entity
|
|
172
|
+
*/
|
|
173
|
+
protected buildKey(id: string): string;
|
|
174
|
+
/**
|
|
175
|
+
* Find an entity by ID
|
|
176
|
+
*/
|
|
177
|
+
findById(id: string): Promise<T | null>;
|
|
178
|
+
/**
|
|
179
|
+
* Find an entity by ID with metadata
|
|
180
|
+
*/
|
|
181
|
+
findByIdWithMetadata(id: string): Promise<KVValueWithMetadata<T, M>>;
|
|
182
|
+
/**
|
|
183
|
+
* Save an entity
|
|
184
|
+
*/
|
|
185
|
+
save(id: string, entity: T, options?: KVPutOptions): Promise<void>;
|
|
186
|
+
/**
|
|
187
|
+
* Delete an entity
|
|
188
|
+
*/
|
|
189
|
+
delete(id: string): Promise<void>;
|
|
190
|
+
/**
|
|
191
|
+
* Check if an entity exists
|
|
192
|
+
*/
|
|
193
|
+
exists(id: string): Promise<boolean>;
|
|
194
|
+
/**
|
|
195
|
+
* Get all entity IDs
|
|
196
|
+
*/
|
|
197
|
+
getAllIds(): Promise<string[]>;
|
|
198
|
+
/**
|
|
199
|
+
* Get all entities
|
|
200
|
+
*/
|
|
201
|
+
findAll(): Promise<T[]>;
|
|
202
|
+
/**
|
|
203
|
+
* Count entities
|
|
204
|
+
*/
|
|
205
|
+
count(): Promise<number>;
|
|
206
|
+
/**
|
|
207
|
+
* Delete all entities
|
|
208
|
+
*/
|
|
209
|
+
deleteAll(): Promise<number>;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Parameter decorator to inject KV namespace directly
|
|
213
|
+
*/
|
|
214
|
+
declare function InjectKV(binding: string): ParameterDecorator;
|
|
215
|
+
/**
|
|
216
|
+
* KV Module for Flareone
|
|
217
|
+
*/
|
|
218
|
+
declare class KVModule {
|
|
219
|
+
/**
|
|
220
|
+
* Configure KV module with static options
|
|
221
|
+
*/
|
|
222
|
+
static forRoot(options: KVModuleOptions): DynamicModule;
|
|
223
|
+
/**
|
|
224
|
+
* Configure KV module with async options
|
|
225
|
+
*/
|
|
226
|
+
static forRootAsync(options: KVModuleAsyncOptions): DynamicModule;
|
|
227
|
+
/**
|
|
228
|
+
* Configure KV for a specific feature/namespace
|
|
229
|
+
*/
|
|
230
|
+
static forFeature(options: KVModuleOptions): DynamicModule;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Create a KV service for a specific namespace
|
|
234
|
+
*/
|
|
235
|
+
declare function createKVService(namespace: KVNamespace, options?: Partial<KVModuleOptions>): KVService;
|
|
236
|
+
/**
|
|
237
|
+
* Helper to get KV namespace from environment
|
|
238
|
+
*/
|
|
239
|
+
declare function getKVNamespace(env: Record<string, unknown>, binding: string): KVNamespace;
|
|
240
|
+
|
|
241
|
+
export { InjectKV, type KVGetOptions, type KVListOptions, type KVListResult, KVModule, type KVModuleAsyncOptions, type KVModuleOptions, type KVPutOptions, KVRepository, KVService, type KVValueWithMetadata, KV_NAMESPACE, KV_OPTIONS, createKVService, getKVNamespace };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import { createToken, Injectable, Module } from '@flareone/core';
|
|
2
|
+
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
5
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
6
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
7
|
+
if (decorator = decorators[i])
|
|
8
|
+
result = (decorator(result)) || result;
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
var KV_OPTIONS = createToken("KV_OPTIONS");
|
|
12
|
+
var KV_NAMESPACE = createToken("KV_NAMESPACE");
|
|
13
|
+
var KVService = class {
|
|
14
|
+
namespace = null;
|
|
15
|
+
options = null;
|
|
16
|
+
/**
|
|
17
|
+
* Initialize with KV namespace and options
|
|
18
|
+
*/
|
|
19
|
+
initialize(namespace, options) {
|
|
20
|
+
this.namespace = namespace;
|
|
21
|
+
this.options = options;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Get the underlying KV namespace
|
|
25
|
+
*/
|
|
26
|
+
getNamespace() {
|
|
27
|
+
if (!this.namespace) {
|
|
28
|
+
throw new Error("KV namespace not initialized. Make sure KVModule is properly configured.");
|
|
29
|
+
}
|
|
30
|
+
return this.namespace;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build the full key with optional prefix
|
|
34
|
+
*/
|
|
35
|
+
buildKey(key) {
|
|
36
|
+
return this.options?.keyPrefix ? `${this.options.keyPrefix}${key}` : key;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get a value by key
|
|
40
|
+
*/
|
|
41
|
+
async get(key, options) {
|
|
42
|
+
const ns = this.getNamespace();
|
|
43
|
+
const fullKey = this.buildKey(key);
|
|
44
|
+
const type = options?.type ?? "json";
|
|
45
|
+
const cacheTtl = options?.cacheTtl ?? this.options?.cacheTtl;
|
|
46
|
+
const kvOptions = { type };
|
|
47
|
+
if (cacheTtl) {
|
|
48
|
+
kvOptions.cacheTtl = cacheTtl;
|
|
49
|
+
}
|
|
50
|
+
return ns.get(fullKey, kvOptions);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get a value as text
|
|
54
|
+
*/
|
|
55
|
+
async getText(key, cacheTtl) {
|
|
56
|
+
return this.get(key, { type: "text", cacheTtl });
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get a value as JSON
|
|
60
|
+
*/
|
|
61
|
+
async getJson(key, cacheTtl) {
|
|
62
|
+
return this.get(key, { type: "json", cacheTtl });
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get a value as ArrayBuffer
|
|
66
|
+
*/
|
|
67
|
+
async getArrayBuffer(key, cacheTtl) {
|
|
68
|
+
return this.get(key, { type: "arrayBuffer", cacheTtl });
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get a value as ReadableStream
|
|
72
|
+
*/
|
|
73
|
+
async getStream(key, cacheTtl) {
|
|
74
|
+
return this.get(key, { type: "stream", cacheTtl });
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get a value with its metadata
|
|
78
|
+
*/
|
|
79
|
+
async getWithMetadata(key, type = "json") {
|
|
80
|
+
const ns = this.getNamespace();
|
|
81
|
+
const fullKey = this.buildKey(key);
|
|
82
|
+
const result = await ns.getWithMetadata(fullKey, { type });
|
|
83
|
+
return {
|
|
84
|
+
value: result.value,
|
|
85
|
+
metadata: result.metadata
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Set a value
|
|
90
|
+
*/
|
|
91
|
+
async set(key, value, options) {
|
|
92
|
+
const ns = this.getNamespace();
|
|
93
|
+
const fullKey = this.buildKey(key);
|
|
94
|
+
const serialized = typeof value === "string" ? value : JSON.stringify(value);
|
|
95
|
+
const kvOptions = {};
|
|
96
|
+
if (options?.expiration) {
|
|
97
|
+
kvOptions.expiration = options.expiration;
|
|
98
|
+
}
|
|
99
|
+
if (options?.expirationTtl ?? this.options?.defaultTtl) {
|
|
100
|
+
kvOptions.expirationTtl = options?.expirationTtl ?? this.options?.defaultTtl;
|
|
101
|
+
}
|
|
102
|
+
if (options?.metadata) {
|
|
103
|
+
kvOptions.metadata = options.metadata;
|
|
104
|
+
}
|
|
105
|
+
await ns.put(fullKey, serialized, kvOptions);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Set multiple values at once
|
|
109
|
+
*/
|
|
110
|
+
async setMany(entries) {
|
|
111
|
+
await Promise.all(
|
|
112
|
+
entries.map(({ key, value, options }) => this.set(key, value, options))
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Set a value with automatic JSON serialization
|
|
117
|
+
*/
|
|
118
|
+
async setJson(key, value, options) {
|
|
119
|
+
return this.set(key, value, options);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Set a value as raw text
|
|
123
|
+
*/
|
|
124
|
+
async setText(key, value, options) {
|
|
125
|
+
const ns = this.getNamespace();
|
|
126
|
+
const fullKey = this.buildKey(key);
|
|
127
|
+
const kvOptions = {};
|
|
128
|
+
if (options?.expiration) kvOptions.expiration = options.expiration;
|
|
129
|
+
if (options?.expirationTtl ?? this.options?.defaultTtl) {
|
|
130
|
+
kvOptions.expirationTtl = options?.expirationTtl ?? this.options?.defaultTtl;
|
|
131
|
+
}
|
|
132
|
+
if (options?.metadata) kvOptions.metadata = options.metadata;
|
|
133
|
+
await ns.put(fullKey, value, kvOptions);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Set a value as ArrayBuffer
|
|
137
|
+
*/
|
|
138
|
+
async setArrayBuffer(key, value, options) {
|
|
139
|
+
const ns = this.getNamespace();
|
|
140
|
+
const fullKey = this.buildKey(key);
|
|
141
|
+
const kvOptions = {};
|
|
142
|
+
if (options?.expiration) kvOptions.expiration = options.expiration;
|
|
143
|
+
if (options?.expirationTtl ?? this.options?.defaultTtl) {
|
|
144
|
+
kvOptions.expirationTtl = options?.expirationTtl ?? this.options?.defaultTtl;
|
|
145
|
+
}
|
|
146
|
+
if (options?.metadata) kvOptions.metadata = options.metadata;
|
|
147
|
+
await ns.put(fullKey, value, kvOptions);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Delete a key
|
|
151
|
+
*/
|
|
152
|
+
async delete(key) {
|
|
153
|
+
const ns = this.getNamespace();
|
|
154
|
+
const fullKey = this.buildKey(key);
|
|
155
|
+
await ns.delete(fullKey);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Delete multiple keys
|
|
159
|
+
*/
|
|
160
|
+
async deleteMany(keys) {
|
|
161
|
+
await Promise.all(keys.map((key) => this.delete(key)));
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Delete all keys with a prefix
|
|
165
|
+
*/
|
|
166
|
+
async deleteByPrefix(prefix) {
|
|
167
|
+
const keys = await this.keys(prefix);
|
|
168
|
+
await this.deleteMany(keys.map((k) => k.name));
|
|
169
|
+
return keys.length;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* List keys
|
|
173
|
+
*/
|
|
174
|
+
async list(options) {
|
|
175
|
+
const ns = this.getNamespace();
|
|
176
|
+
const kvOptions = {};
|
|
177
|
+
if (options?.prefix) {
|
|
178
|
+
kvOptions.prefix = this.buildKey(options.prefix);
|
|
179
|
+
}
|
|
180
|
+
if (options?.limit) {
|
|
181
|
+
kvOptions.limit = options.limit;
|
|
182
|
+
}
|
|
183
|
+
if (options?.cursor) {
|
|
184
|
+
kvOptions.cursor = options.cursor;
|
|
185
|
+
}
|
|
186
|
+
return ns.list(kvOptions);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get all keys (handles pagination automatically)
|
|
190
|
+
*/
|
|
191
|
+
async keys(prefix) {
|
|
192
|
+
const allKeys = [];
|
|
193
|
+
let cursor;
|
|
194
|
+
let complete = false;
|
|
195
|
+
while (!complete) {
|
|
196
|
+
const result = await this.list({
|
|
197
|
+
prefix,
|
|
198
|
+
cursor,
|
|
199
|
+
limit: 1e3
|
|
200
|
+
});
|
|
201
|
+
allKeys.push(...result.keys);
|
|
202
|
+
complete = result.list_complete;
|
|
203
|
+
cursor = result.cursor;
|
|
204
|
+
}
|
|
205
|
+
return allKeys;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Check if a key exists
|
|
209
|
+
*/
|
|
210
|
+
async exists(key) {
|
|
211
|
+
const value = await this.get(key, { type: "text" });
|
|
212
|
+
return value !== null;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Get or set a value (cache pattern)
|
|
216
|
+
*/
|
|
217
|
+
async getOrSet(key, factory, options) {
|
|
218
|
+
const existing = await this.get(key);
|
|
219
|
+
if (existing !== null) {
|
|
220
|
+
return existing;
|
|
221
|
+
}
|
|
222
|
+
const value = await factory();
|
|
223
|
+
await this.set(key, value, options);
|
|
224
|
+
return value;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Increment a numeric value
|
|
228
|
+
*/
|
|
229
|
+
async increment(key, delta = 1) {
|
|
230
|
+
const current = await this.get(key);
|
|
231
|
+
const newValue = (current ?? 0) + delta;
|
|
232
|
+
await this.set(key, newValue);
|
|
233
|
+
return newValue;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Decrement a numeric value
|
|
237
|
+
*/
|
|
238
|
+
async decrement(key, delta = 1) {
|
|
239
|
+
return this.increment(key, -delta);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Append to an array stored in KV
|
|
243
|
+
*/
|
|
244
|
+
async append(key, item, options) {
|
|
245
|
+
const existing = await this.get(key) ?? [];
|
|
246
|
+
existing.push(item);
|
|
247
|
+
await this.set(key, existing, options);
|
|
248
|
+
return existing;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Update a value with a transformer function
|
|
252
|
+
*/
|
|
253
|
+
async update(key, updater, options) {
|
|
254
|
+
const current = await this.get(key);
|
|
255
|
+
const updated = updater(current);
|
|
256
|
+
await this.set(key, updated, options);
|
|
257
|
+
return updated;
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
KVService = __decorateClass([
|
|
261
|
+
Injectable()
|
|
262
|
+
], KVService);
|
|
263
|
+
var KVRepository = class {
|
|
264
|
+
constructor(kv, prefix) {
|
|
265
|
+
this.kv = kv;
|
|
266
|
+
this.prefix = prefix;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Build the full key for an entity
|
|
270
|
+
*/
|
|
271
|
+
buildKey(id) {
|
|
272
|
+
return `${this.prefix}:${id}`;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Find an entity by ID
|
|
276
|
+
*/
|
|
277
|
+
async findById(id) {
|
|
278
|
+
return this.kv.get(this.buildKey(id));
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Find an entity by ID with metadata
|
|
282
|
+
*/
|
|
283
|
+
async findByIdWithMetadata(id) {
|
|
284
|
+
return this.kv.getWithMetadata(this.buildKey(id));
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Save an entity
|
|
288
|
+
*/
|
|
289
|
+
async save(id, entity, options) {
|
|
290
|
+
await this.kv.set(this.buildKey(id), entity, options);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Delete an entity
|
|
294
|
+
*/
|
|
295
|
+
async delete(id) {
|
|
296
|
+
await this.kv.delete(this.buildKey(id));
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Check if an entity exists
|
|
300
|
+
*/
|
|
301
|
+
async exists(id) {
|
|
302
|
+
return this.kv.exists(this.buildKey(id));
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Get all entity IDs
|
|
306
|
+
*/
|
|
307
|
+
async getAllIds() {
|
|
308
|
+
const keys = await this.kv.keys(`${this.prefix}:`);
|
|
309
|
+
return keys.map((k) => k.name.replace(`${this.prefix}:`, ""));
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Get all entities
|
|
313
|
+
*/
|
|
314
|
+
async findAll() {
|
|
315
|
+
const ids = await this.getAllIds();
|
|
316
|
+
const entities = await Promise.all(ids.map((id) => this.findById(id)));
|
|
317
|
+
return entities.filter((e) => e !== null);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Count entities
|
|
321
|
+
*/
|
|
322
|
+
async count() {
|
|
323
|
+
const keys = await this.kv.keys(`${this.prefix}:`);
|
|
324
|
+
return keys.length;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Delete all entities
|
|
328
|
+
*/
|
|
329
|
+
async deleteAll() {
|
|
330
|
+
return this.kv.deleteByPrefix(`${this.prefix}:`);
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
function InjectKV(binding) {
|
|
334
|
+
return (target, propertyKey, parameterIndex) => {
|
|
335
|
+
const metaKey = `__kv_bindings_${String(propertyKey)}`;
|
|
336
|
+
const existingParams = target[metaKey] ?? [];
|
|
337
|
+
existingParams.push({ index: parameterIndex, binding });
|
|
338
|
+
target[metaKey] = existingParams;
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
var KVModule = class {
|
|
342
|
+
/**
|
|
343
|
+
* Configure KV module with static options
|
|
344
|
+
*/
|
|
345
|
+
static forRoot(options) {
|
|
346
|
+
return {
|
|
347
|
+
module: KVModule,
|
|
348
|
+
providers: [
|
|
349
|
+
{ provide: KV_OPTIONS, useValue: options },
|
|
350
|
+
KVService
|
|
351
|
+
],
|
|
352
|
+
exports: [KVService, KV_OPTIONS]
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Configure KV module with async options
|
|
357
|
+
*/
|
|
358
|
+
static forRootAsync(options) {
|
|
359
|
+
return {
|
|
360
|
+
module: KVModule,
|
|
361
|
+
imports: options.imports ?? [],
|
|
362
|
+
providers: [
|
|
363
|
+
{
|
|
364
|
+
provide: KV_OPTIONS,
|
|
365
|
+
useFactory: options.useFactory,
|
|
366
|
+
inject: options.inject
|
|
367
|
+
},
|
|
368
|
+
KVService
|
|
369
|
+
],
|
|
370
|
+
exports: [KVService, KV_OPTIONS]
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Configure KV for a specific feature/namespace
|
|
375
|
+
*/
|
|
376
|
+
static forFeature(options) {
|
|
377
|
+
const featureToken = createToken(`KV_${options.binding}`);
|
|
378
|
+
return {
|
|
379
|
+
module: KVModule,
|
|
380
|
+
providers: [
|
|
381
|
+
{ provide: featureToken, useClass: KVService },
|
|
382
|
+
{ provide: `KV_OPTIONS_${options.binding}`, useValue: options }
|
|
383
|
+
],
|
|
384
|
+
exports: [featureToken]
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
KVModule = __decorateClass([
|
|
389
|
+
Module({})
|
|
390
|
+
], KVModule);
|
|
391
|
+
function createKVService(namespace, options = {}) {
|
|
392
|
+
const service = new KVService();
|
|
393
|
+
service.initialize(namespace, {
|
|
394
|
+
binding: "custom",
|
|
395
|
+
...options
|
|
396
|
+
});
|
|
397
|
+
return service;
|
|
398
|
+
}
|
|
399
|
+
function getKVNamespace(env, binding) {
|
|
400
|
+
const namespace = env[binding];
|
|
401
|
+
if (!namespace) {
|
|
402
|
+
throw new Error(`KV namespace '${binding}' not found in environment`);
|
|
403
|
+
}
|
|
404
|
+
return namespace;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export { InjectKV, KVModule, KVRepository, KVService, KV_NAMESPACE, KV_OPTIONS, createKVService, getKVNamespace };
|
|
408
|
+
//# sourceMappingURL=index.js.map
|
|
409
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * @flareone/kv - Workers KV Integration\r\n * High-level, type-safe wrapper for Cloudflare Workers KV\r\n */\r\n\r\nimport {\r\n Injectable,\r\n Module,\r\n createToken,\r\n type Type,\r\n type DynamicModule,\r\n type InjectionToken,\r\n} from '@flareone/core';\r\n\r\nexport interface KVGetOptions {\r\n type?: 'text' | 'json' | 'arrayBuffer' | 'stream';\r\n cacheTtl?: number;\r\n}\r\n\r\nexport interface KVPutOptions {\r\n expiration?: number;\r\n expirationTtl?: number;\r\n metadata?: Record<string, unknown>;\r\n}\r\n\r\nexport interface KVListOptions {\r\n prefix?: string;\r\n limit?: number;\r\n cursor?: string;\r\n}\r\nexport interface KVListResult<T = unknown> {\r\n keys: Array<{\r\n name: string;\r\n expiration?: number;\r\n metadata?: T;\r\n }>;\r\n list_complete: boolean;\r\n cursor?: string;\r\n}\r\n\r\nexport interface KVValueWithMetadata<T, M = unknown> {\r\n value: T | null;\r\n metadata: M | null;\r\n}\r\n\r\nexport interface KVModuleOptions {\r\n binding: string;\r\n defaultTtl?: number;\r\n keyPrefix?: string;\r\n enableCache?: boolean;\r\n cacheTtl?: number;\r\n}\r\n\r\nexport interface KVModuleAsyncOptions {\r\n imports?: Array<Type | DynamicModule>;\r\n useFactory: (...args: unknown[]) => KVModuleOptions | Promise<KVModuleOptions>;\r\n inject?: InjectionToken[];\r\n}\r\n\r\nexport const KV_OPTIONS = createToken<KVModuleOptions>('KV_OPTIONS');\r\nexport const KV_NAMESPACE = createToken<KVNamespace>('KV_NAMESPACE');\r\n\r\n/**\r\n * High-level KV service with type-safe operations\r\n */\r\n@Injectable()\r\nexport class KVService {\r\n private namespace: KVNamespace | null = null;\r\n private options: KVModuleOptions | null = null;\r\n\r\n /**\r\n * Initialize with KV namespace and options\r\n */\r\n initialize(namespace: KVNamespace, options: KVModuleOptions): void {\r\n this.namespace = namespace;\r\n this.options = options;\r\n }\r\n\r\n /**\r\n * Get the underlying KV namespace\r\n */\r\n getNamespace(): KVNamespace {\r\n if (!this.namespace) {\r\n throw new Error('KV namespace not initialized. Make sure KVModule is properly configured.');\r\n }\r\n return this.namespace;\r\n }\r\n\r\n /**\r\n * Build the full key with optional prefix\r\n */\r\n private buildKey(key: string): string {\r\n return this.options?.keyPrefix ? `${this.options.keyPrefix}${key}` : key;\r\n }\r\n\r\n /**\r\n * Get a value by key\r\n */\r\n async get<T = string>(key: string, options?: KVGetOptions): Promise<T | null> {\r\n const ns = this.getNamespace();\r\n const fullKey = this.buildKey(key);\r\n const type = options?.type ?? 'json';\r\n const cacheTtl = options?.cacheTtl ?? this.options?.cacheTtl;\r\n\r\n const kvOptions: KVNamespaceGetOptions<any> = { type };\r\n if (cacheTtl) {\r\n kvOptions.cacheTtl = cacheTtl;\r\n }\r\n\r\n return ns.get(fullKey, kvOptions) as Promise<T | null>;\r\n }\r\n\r\n /**\r\n * Get a value as text\r\n */\r\n async getText(key: string, cacheTtl?: number): Promise<string | null> {\r\n return this.get<string>(key, { type: 'text', cacheTtl });\r\n }\r\n\r\n /**\r\n * Get a value as JSON\r\n */\r\n async getJson<T>(key: string, cacheTtl?: number): Promise<T | null> {\r\n return this.get<T>(key, { type: 'json', cacheTtl });\r\n }\r\n\r\n /**\r\n * Get a value as ArrayBuffer\r\n */\r\n async getArrayBuffer(key: string, cacheTtl?: number): Promise<ArrayBuffer | null> {\r\n return this.get<ArrayBuffer>(key, { type: 'arrayBuffer', cacheTtl });\r\n }\r\n\r\n /**\r\n * Get a value as ReadableStream\r\n */\r\n async getStream(key: string, cacheTtl?: number): Promise<ReadableStream | null> {\r\n return this.get<ReadableStream>(key, { type: 'stream', cacheTtl });\r\n }\r\n\r\n /**\r\n * Get a value with its metadata\r\n */\r\n async getWithMetadata<T = unknown, M = unknown>(\r\n key: string,\r\n type: 'text' | 'json' | 'arrayBuffer' | 'stream' = 'json'\r\n ): Promise<KVValueWithMetadata<T, M>> {\r\n const ns = this.getNamespace();\r\n const fullKey = this.buildKey(key);\r\n const result = await (ns.getWithMetadata as (key: string, options: { type: string }) => Promise<{ value: unknown; metadata: unknown }>)(fullKey, { type });\r\n return {\r\n value: result.value as T | null,\r\n metadata: result.metadata as M | null,\r\n };\r\n }\r\n\r\n /**\r\n * Set a value\r\n */\r\n async set<T = unknown>(\r\n key: string,\r\n value: T,\r\n options?: KVPutOptions\r\n ): Promise<void> {\r\n const ns = this.getNamespace();\r\n const fullKey = this.buildKey(key);\r\n\r\n const serialized = typeof value === 'string' ? value : JSON.stringify(value);\r\n\r\n const kvOptions: KVNamespacePutOptions = {};\r\n if (options?.expiration) {\r\n kvOptions.expiration = options.expiration;\r\n }\r\n if (options?.expirationTtl ?? this.options?.defaultTtl) {\r\n kvOptions.expirationTtl = options?.expirationTtl ?? this.options?.defaultTtl;\r\n }\r\n if (options?.metadata) {\r\n kvOptions.metadata = options.metadata;\r\n }\r\n\r\n await ns.put(fullKey, serialized, kvOptions);\r\n }\r\n\r\n /**\r\n * Set multiple values at once\r\n */\r\n async setMany<T = unknown>(\r\n entries: Array<{ key: string; value: T; options?: KVPutOptions }>\r\n ): Promise<void> {\r\n await Promise.all(\r\n entries.map(({ key, value, options }) => this.set(key, value, options))\r\n );\r\n }\r\n\r\n /**\r\n * Set a value with automatic JSON serialization\r\n */\r\n async setJson<T>(key: string, value: T, options?: KVPutOptions): Promise<void> {\r\n return this.set(key, value, options);\r\n }\r\n\r\n /**\r\n * Set a value as raw text\r\n */\r\n async setText(key: string, value: string, options?: KVPutOptions): Promise<void> {\r\n const ns = this.getNamespace();\r\n const fullKey = this.buildKey(key);\r\n\r\n const kvOptions: KVNamespacePutOptions = {};\r\n if (options?.expiration) kvOptions.expiration = options.expiration;\r\n if (options?.expirationTtl ?? this.options?.defaultTtl) {\r\n kvOptions.expirationTtl = options?.expirationTtl ?? this.options?.defaultTtl;\r\n }\r\n if (options?.metadata) kvOptions.metadata = options.metadata;\r\n\r\n await ns.put(fullKey, value, kvOptions);\r\n }\r\n\r\n /**\r\n * Set a value as ArrayBuffer\r\n */\r\n async setArrayBuffer(\r\n key: string,\r\n value: ArrayBuffer,\r\n options?: KVPutOptions\r\n ): Promise<void> {\r\n const ns = this.getNamespace();\r\n const fullKey = this.buildKey(key);\r\n\r\n const kvOptions: KVNamespacePutOptions = {};\r\n if (options?.expiration) kvOptions.expiration = options.expiration;\r\n if (options?.expirationTtl ?? this.options?.defaultTtl) {\r\n kvOptions.expirationTtl = options?.expirationTtl ?? this.options?.defaultTtl;\r\n }\r\n if (options?.metadata) kvOptions.metadata = options.metadata;\r\n\r\n await ns.put(fullKey, value, kvOptions);\r\n }\r\n\r\n /**\r\n * Delete a key\r\n */\r\n async delete(key: string): Promise<void> {\r\n const ns = this.getNamespace();\r\n const fullKey = this.buildKey(key);\r\n await ns.delete(fullKey);\r\n }\r\n\r\n /**\r\n * Delete multiple keys\r\n */\r\n async deleteMany(keys: string[]): Promise<void> {\r\n await Promise.all(keys.map((key) => this.delete(key)));\r\n }\r\n\r\n /**\r\n * Delete all keys with a prefix\r\n */\r\n async deleteByPrefix(prefix: string): Promise<number> {\r\n const keys = await this.keys(prefix);\r\n await this.deleteMany(keys.map((k) => k.name));\r\n return keys.length;\r\n }\r\n\r\n /**\r\n * List keys\r\n */\r\n async list<M = unknown>(options?: KVListOptions): Promise<KVListResult<M>> {\r\n const ns = this.getNamespace();\r\n\r\n const kvOptions: KVNamespaceListOptions = {};\r\n if (options?.prefix) {\r\n kvOptions.prefix = this.buildKey(options.prefix);\r\n }\r\n if (options?.limit) {\r\n kvOptions.limit = options.limit;\r\n }\r\n if (options?.cursor) {\r\n kvOptions.cursor = options.cursor;\r\n }\r\n\r\n return ns.list(kvOptions) as Promise<KVListResult<M>>;\r\n }\r\n\r\n /**\r\n * Get all keys (handles pagination automatically)\r\n */\r\n async keys<M = unknown>(prefix?: string): Promise<Array<{ name: string; metadata?: M }>> {\r\n const allKeys: Array<{ name: string; metadata?: M }> = [];\r\n let cursor: string | undefined;\r\n let complete = false;\r\n\r\n while (!complete) {\r\n const result = await this.list<M>({\r\n prefix,\r\n cursor,\r\n limit: 1000,\r\n });\r\n\r\n allKeys.push(...result.keys);\r\n complete = result.list_complete;\r\n cursor = result.cursor;\r\n }\r\n\r\n return allKeys;\r\n }\r\n\r\n /**\r\n * Check if a key exists\r\n */\r\n async exists(key: string): Promise<boolean> {\r\n const value = await this.get(key, { type: 'text' });\r\n return value !== null;\r\n }\r\n\r\n /**\r\n * Get or set a value (cache pattern)\r\n */\r\n async getOrSet<T>(\r\n key: string,\r\n factory: () => T | Promise<T>,\r\n options?: KVPutOptions\r\n ): Promise<T> {\r\n const existing = await this.get<T>(key);\r\n if (existing !== null) {\r\n return existing;\r\n }\r\n\r\n const value = await factory();\r\n await this.set(key, value, options);\r\n return value;\r\n }\r\n\r\n /**\r\n * Increment a numeric value\r\n */\r\n async increment(key: string, delta: number = 1): Promise<number> {\r\n const current = await this.get<number>(key);\r\n const newValue = (current ?? 0) + delta;\r\n await this.set(key, newValue);\r\n return newValue;\r\n }\r\n\r\n /**\r\n * Decrement a numeric value\r\n */\r\n async decrement(key: string, delta: number = 1): Promise<number> {\r\n return this.increment(key, -delta);\r\n }\r\n\r\n /**\r\n * Append to an array stored in KV\r\n */\r\n async append<T>(key: string, item: T, options?: KVPutOptions): Promise<T[]> {\r\n const existing = await this.get<T[]>(key) ?? [];\r\n existing.push(item);\r\n await this.set(key, existing, options);\r\n return existing;\r\n }\r\n\r\n /**\r\n * Update a value with a transformer function\r\n */\r\n async update<T>(\r\n key: string,\r\n updater: (current: T | null) => T,\r\n options?: KVPutOptions\r\n ): Promise<T> {\r\n const current = await this.get<T>(key);\r\n const updated = updater(current);\r\n await this.set(key, updated, options);\r\n return updated;\r\n }\r\n}\r\n\r\n/**\r\n * Base class for creating typed KV repositories\r\n */\r\nexport abstract class KVRepository<T, M = unknown> {\r\n constructor(\r\n protected readonly kv: KVService,\r\n protected readonly prefix: string\r\n ) { }\r\n\r\n /**\r\n * Build the full key for an entity\r\n */\r\n protected buildKey(id: string): string {\r\n return `${this.prefix}:${id}`;\r\n }\r\n\r\n /**\r\n * Find an entity by ID\r\n */\r\n async findById(id: string): Promise<T | null> {\r\n return this.kv.get<T>(this.buildKey(id));\r\n }\r\n\r\n /**\r\n * Find an entity by ID with metadata\r\n */\r\n async findByIdWithMetadata(id: string): Promise<KVValueWithMetadata<T, M>> {\r\n return this.kv.getWithMetadata<T, M>(this.buildKey(id));\r\n }\r\n\r\n /**\r\n * Save an entity\r\n */\r\n async save(id: string, entity: T, options?: KVPutOptions): Promise<void> {\r\n await this.kv.set(this.buildKey(id), entity, options);\r\n }\r\n\r\n /**\r\n * Delete an entity\r\n */\r\n async delete(id: string): Promise<void> {\r\n await this.kv.delete(this.buildKey(id));\r\n }\r\n\r\n /**\r\n * Check if an entity exists\r\n */\r\n async exists(id: string): Promise<boolean> {\r\n return this.kv.exists(this.buildKey(id));\r\n }\r\n\r\n /**\r\n * Get all entity IDs\r\n */\r\n async getAllIds(): Promise<string[]> {\r\n const keys = await this.kv.keys(`${this.prefix}:`);\r\n return keys.map((k) => k.name.replace(`${this.prefix}:`, ''));\r\n }\r\n\r\n /**\r\n * Get all entities\r\n */\r\n async findAll(): Promise<T[]> {\r\n const ids = await this.getAllIds();\r\n const entities = await Promise.all(ids.map((id) => this.findById(id)));\r\n return entities.filter((e): e is NonNullable<typeof e> => e !== null) as T[];\r\n }\r\n\r\n /**\r\n * Count entities\r\n */\r\n async count(): Promise<number> {\r\n const keys = await this.kv.keys(`${this.prefix}:`);\r\n return keys.length;\r\n }\r\n\r\n /**\r\n * Delete all entities\r\n */\r\n async deleteAll(): Promise<number> {\r\n return this.kv.deleteByPrefix(`${this.prefix}:`);\r\n }\r\n}\r\n\r\n/**\r\n * Parameter decorator to inject KV namespace directly\r\n */\r\nexport function InjectKV(binding: string): ParameterDecorator {\r\n return (target, propertyKey, parameterIndex) => {\r\n const metaKey = `__kv_bindings_${String(propertyKey)}`;\r\n const existingParams = (target as Record<string, unknown>)[metaKey] as Array<{ index: number; binding: string }> ?? [];\r\n existingParams.push({ index: parameterIndex, binding });\r\n (target as Record<string, unknown>)[metaKey] = existingParams;\r\n };\r\n}\r\n\r\n/**\r\n * KV Module for Flareone\r\n */\r\n@Module({})\r\nexport class KVModule {\r\n /**\r\n * Configure KV module with static options\r\n */\r\n static forRoot(options: KVModuleOptions): DynamicModule {\r\n return {\r\n module: KVModule,\r\n providers: [\r\n { provide: KV_OPTIONS, useValue: options },\r\n KVService,\r\n ],\r\n exports: [KVService, KV_OPTIONS],\r\n };\r\n }\r\n\r\n /**\r\n * Configure KV module with async options\r\n */\r\n static forRootAsync(options: KVModuleAsyncOptions): DynamicModule {\r\n return {\r\n module: KVModule,\r\n imports: options.imports ?? [],\r\n providers: [\r\n {\r\n provide: KV_OPTIONS,\r\n useFactory: options.useFactory,\r\n inject: options.inject,\r\n },\r\n KVService,\r\n ],\r\n exports: [KVService, KV_OPTIONS],\r\n };\r\n }\r\n\r\n /**\r\n * Configure KV for a specific feature/namespace\r\n */\r\n static forFeature(options: KVModuleOptions): DynamicModule {\r\n const featureToken = createToken<KVService>(`KV_${options.binding}`);\r\n\r\n return {\r\n module: KVModule,\r\n providers: [\r\n { provide: featureToken, useClass: KVService },\r\n { provide: `KV_OPTIONS_${options.binding}`, useValue: options },\r\n ],\r\n exports: [featureToken],\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Create a KV service for a specific namespace\r\n */\r\nexport function createKVService(\r\n namespace: KVNamespace,\r\n options: Partial<KVModuleOptions> = {}\r\n): KVService {\r\n const service = new KVService();\r\n service.initialize(namespace, {\r\n binding: 'custom',\r\n ...options,\r\n });\r\n return service;\r\n}\r\n\r\n/**\r\n * Helper to get KV namespace from environment\r\n */\r\nexport function getKVNamespace(env: Record<string, unknown>, binding: string): KVNamespace {\r\n const namespace = env[binding] as KVNamespace | undefined;\r\n if (!namespace) {\r\n throw new Error(`KV namespace '${binding}' not found in environment`);\r\n }\r\n return namespace;\r\n}\r\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flareone/kv",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Workers KV integration for Flareone framework",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cloudflare",
|
|
7
|
+
"workers",
|
|
8
|
+
"kv",
|
|
9
|
+
"flareon",
|
|
10
|
+
"key-value",
|
|
11
|
+
"storage"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://flareone.dev",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "Flareone Contributors",
|
|
16
|
+
"type": "module",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"main": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@flareone/core": "0.1.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@cloudflare/workers-types": "^4.20250109.0",
|
|
34
|
+
"rimraf": "^6.0.0",
|
|
35
|
+
"tsup": "^8.3.0",
|
|
36
|
+
"typescript": "^5.7.0"
|
|
37
|
+
},
|
|
38
|
+
"sideEffects": false,
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsup",
|
|
41
|
+
"clean": "rimraf dist",
|
|
42
|
+
"dev": "tsup --watch",
|
|
43
|
+
"typecheck": "tsc --noEmit"
|
|
44
|
+
}
|
|
45
|
+
}
|