@firela/runtime-adapters 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 +156 -0
- package/dist/cloudflare.d.ts +79 -0
- package/dist/cloudflare.d.ts.map +1 -0
- package/dist/cloudflare.js +119 -0
- package/dist/cloudflare.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/node.d.ts +80 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +118 -0
- package/dist/node.js.map +1 -0
- package/dist/types.d.ts +139 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 fire-la
|
|
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,156 @@
|
|
|
1
|
+
# @firela/runtime-adapters
|
|
2
|
+
|
|
3
|
+
Platform-agnostic runtime adapters for Cloudflare Workers, Node.js, and edge runtimes.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This package provides a unified abstraction layer for runtime-specific capabilities, allowing your code to run seamlessly across different JavaScript environments:
|
|
8
|
+
|
|
9
|
+
- **Cloudflare Workers** - KV storage, Web Crypto API
|
|
10
|
+
- **Node.js** - File-based storage, Node crypto module
|
|
11
|
+
- **Edge runtimes** - Any environment implementing the adapter interfaces
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @firela/runtime-adapters
|
|
17
|
+
# or
|
|
18
|
+
pnpm add @firela/runtime-adapters
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### Cloudflare Workers
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { createCloudflareAdapter } from '@firela/runtime-adapters/cloudflare'
|
|
27
|
+
|
|
28
|
+
export default {
|
|
29
|
+
async fetch(request, env, ctx) {
|
|
30
|
+
const adapter = createCloudflareAdapter(env)
|
|
31
|
+
|
|
32
|
+
// Use KV store
|
|
33
|
+
await adapter.kv.set('session:abc', { userId: 'user1' }, { ttl: 3600000 })
|
|
34
|
+
|
|
35
|
+
// Use crypto
|
|
36
|
+
const signature = await adapter.crypto.hmacSha256('payload', 'secret')
|
|
37
|
+
|
|
38
|
+
// Log messages
|
|
39
|
+
adapter.logger.info('Request processed')
|
|
40
|
+
|
|
41
|
+
return new Response('OK')
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Node.js
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { createNodeAdapter } from '@firela/runtime-adapters/node'
|
|
50
|
+
|
|
51
|
+
const adapter = createNodeAdapter()
|
|
52
|
+
|
|
53
|
+
// Use in-memory KV store (useful for development/testing)
|
|
54
|
+
await adapter.kv.set('key', 'value')
|
|
55
|
+
|
|
56
|
+
// Use Node.js crypto
|
|
57
|
+
const bytes = adapter.crypto.randomBytes(16)
|
|
58
|
+
|
|
59
|
+
// Console logging
|
|
60
|
+
adapter.logger.info('Application started')
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## API Reference
|
|
64
|
+
|
|
65
|
+
### Types
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
interface KVStore {
|
|
69
|
+
get<T = unknown>(key: string): Promise<T | null>
|
|
70
|
+
set<T>(key: string, value: T, options?: { ttl?: number }): Promise<void>
|
|
71
|
+
delete(key: string): Promise<boolean>
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface CryptoAdapter {
|
|
75
|
+
hmacSha256(data: string, secret: string): Promise<string>
|
|
76
|
+
randomBytes(length: number): Uint8Array
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface Logger {
|
|
80
|
+
debug(message: string, ...args: unknown[]): void
|
|
81
|
+
info(message: string, ...args: unknown[]): void
|
|
82
|
+
warn(message: string, ...args: unknown[]): void
|
|
83
|
+
error(message: string, ...args: unknown[]): void
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface RuntimeAdapter {
|
|
87
|
+
kv: KVStore
|
|
88
|
+
crypto: CryptoAdapter
|
|
89
|
+
logger: Logger
|
|
90
|
+
platform: string
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Cloudflare Adapter
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { createCloudflareAdapter } from '@firela/runtime-adapters/cloudflare'
|
|
98
|
+
|
|
99
|
+
const adapter = createCloudflareAdapter(env, 'KV') // 'KV' is the default namespace
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Features:**
|
|
103
|
+
- `CloudflareKVStore` - Wraps KVNamespace for key-value storage
|
|
104
|
+
- `CloudflareCryptoAdapter` - Uses Web Crypto API
|
|
105
|
+
- `ConsoleLogger` - Console-based logging
|
|
106
|
+
|
|
107
|
+
### Node.js Adapter
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import { createNodeAdapter } from '@firela/runtime-adapters/node'
|
|
111
|
+
|
|
112
|
+
const adapter = createNodeAdapter()
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Features:**
|
|
116
|
+
- `MemoryKVStore` - In-memory key-value storage (useful for development)
|
|
117
|
+
- `NodeCryptoAdapter` - Uses Node.js crypto module
|
|
118
|
+
- `ConsoleLogger` - Console-based logging
|
|
119
|
+
|
|
120
|
+
## Usage Examples
|
|
121
|
+
|
|
122
|
+
### Rate Limiting with KV Storage
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import { createCloudflareAdapter } from '@firela/runtime-adapters/cloudflare'
|
|
126
|
+
|
|
127
|
+
const adapter = createCloudflareAdapter(env)
|
|
128
|
+
|
|
129
|
+
async function checkRateLimit(key: string, limit: number, windowMs: number): Promise<boolean> {
|
|
130
|
+
const count = await adapter.kv.get<number>(key) || 0
|
|
131
|
+
|
|
132
|
+
if (count >= limit) {
|
|
133
|
+
return false
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
await adapter.kv.set(key, count + 1, { ttl: windowMs })
|
|
137
|
+
return true
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### HMAC Signature Verification
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import { createCloudflareAdapter } from '@firela/runtime-adapters/cloudflare'
|
|
145
|
+
|
|
146
|
+
const adapter = createCloudflareAdapter(env)
|
|
147
|
+
|
|
148
|
+
async function verifySignature(payload: string, signature: string, secret: string): Promise<boolean> {
|
|
149
|
+
const expected = await adapter.crypto.hmacSha256(payload, secret)
|
|
150
|
+
return expected === signature
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## License
|
|
155
|
+
|
|
156
|
+
MIT
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Workers Runtime Adapter
|
|
3
|
+
*
|
|
4
|
+
* Provides RuntimeAdapter implementation for Cloudflare Workers environment:
|
|
5
|
+
* - KVNamespace for KVStore
|
|
6
|
+
* - Web Crypto API for CryptoAdapter
|
|
7
|
+
* - Console for Logger
|
|
8
|
+
*/
|
|
9
|
+
import type { KVStore, CryptoAdapter, Logger, RuntimeAdapter } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* CloudflareKVStore - KVNamespace wrapper implementing KVStore interface
|
|
12
|
+
*
|
|
13
|
+
* Uses Cloudflare KV for persistent storage with TTL support.
|
|
14
|
+
*/
|
|
15
|
+
export declare class CloudflareKVStore implements KVStore {
|
|
16
|
+
private kv;
|
|
17
|
+
constructor(kv: KVNamespace);
|
|
18
|
+
get<T = unknown>(key: string): Promise<T | null>;
|
|
19
|
+
set<T>(key: string, value: T, options?: {
|
|
20
|
+
ttl?: number;
|
|
21
|
+
}): Promise<void>;
|
|
22
|
+
delete(key: string): Promise<boolean>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* CloudflareCryptoAdapter - Web Crypto API implementation
|
|
26
|
+
*
|
|
27
|
+
* Uses the Web Crypto API available in Cloudflare Workers.
|
|
28
|
+
*/
|
|
29
|
+
export declare class CloudflareCryptoAdapter implements CryptoAdapter {
|
|
30
|
+
hmacSha256(data: string, secret: string): Promise<string>;
|
|
31
|
+
randomBytes(length: number): Uint8Array;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* ConsoleLogger - Console-based logger for Cloudflare Workers
|
|
35
|
+
*/
|
|
36
|
+
export declare class ConsoleLogger implements Logger {
|
|
37
|
+
debug(message: string, ...args: unknown[]): void;
|
|
38
|
+
info(message: string, ...args: unknown[]): void;
|
|
39
|
+
warn(message: string, ...args: unknown[]): void;
|
|
40
|
+
error(message: string, ...args: unknown[]): void;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Cloudflare environment bindings type
|
|
44
|
+
*
|
|
45
|
+
* Extend this interface to include your specific bindings.
|
|
46
|
+
*/
|
|
47
|
+
export interface CloudflareEnv {
|
|
48
|
+
/** Cloudflare KV namespace binding */
|
|
49
|
+
KV?: KVNamespace;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create a RuntimeAdapter for Cloudflare Workers
|
|
53
|
+
*
|
|
54
|
+
* @param env - Cloudflare environment bindings (includes KV namespace)
|
|
55
|
+
* @param kvNamespace - Optional KV namespace name (defaults to 'KV')
|
|
56
|
+
* @returns RuntimeAdapter instance configured for Cloudflare Workers
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* import { createCloudflareAdapter } from '@firela/runtime-adapters/cloudflare'
|
|
61
|
+
*
|
|
62
|
+
* export default {
|
|
63
|
+
* async fetch(request, env, ctx) {
|
|
64
|
+
* const adapter = createCloudflareAdapter(env)
|
|
65
|
+
*
|
|
66
|
+
* // Use KV store
|
|
67
|
+
* await adapter.kv.set('session:abc', { userId: 'user1' }, { ttl: 3600000 })
|
|
68
|
+
*
|
|
69
|
+
* // Use crypto
|
|
70
|
+
* const signature = await adapter.crypto.hmacSha256('payload', 'secret')
|
|
71
|
+
*
|
|
72
|
+
* return new Response('OK')
|
|
73
|
+
* }
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare function createCloudflareAdapter(env: CloudflareEnv, kvNamespace?: string): RuntimeAdapter;
|
|
78
|
+
export type { KVStore, CryptoAdapter, Logger, RuntimeAdapter } from './types.js';
|
|
79
|
+
//# sourceMappingURL=cloudflare.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../src/cloudflare.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhF;;;;GAIG;AACH,qBAAa,iBAAkB,YAAW,OAAO;IACnC,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,WAAW;IAE7B,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAKhD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAUxE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAO5C;AAED;;;;GAIG;AACH,qBAAa,uBAAwB,YAAW,aAAa;IACrD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAwB/D,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU;CAKxC;AAED;;GAEG;AACH,qBAAa,aAAc,YAAW,MAAM;IAC1C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAIhD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAI/C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAI/C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;CAGjD;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,sCAAsC;IACtC,EAAE,CAAC,EAAE,WAAW,CAAA;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,aAAa,EAClB,WAAW,GAAE,MAAa,GACzB,cAAc,CAchB;AAGD,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Workers Runtime Adapter
|
|
3
|
+
*
|
|
4
|
+
* Provides RuntimeAdapter implementation for Cloudflare Workers environment:
|
|
5
|
+
* - KVNamespace for KVStore
|
|
6
|
+
* - Web Crypto API for CryptoAdapter
|
|
7
|
+
* - Console for Logger
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* CloudflareKVStore - KVNamespace wrapper implementing KVStore interface
|
|
11
|
+
*
|
|
12
|
+
* Uses Cloudflare KV for persistent storage with TTL support.
|
|
13
|
+
*/
|
|
14
|
+
export class CloudflareKVStore {
|
|
15
|
+
kv;
|
|
16
|
+
constructor(kv) {
|
|
17
|
+
this.kv = kv;
|
|
18
|
+
}
|
|
19
|
+
async get(key) {
|
|
20
|
+
const value = await this.kv.get(key, 'json');
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
async set(key, value, options) {
|
|
24
|
+
if (options?.ttl !== undefined) {
|
|
25
|
+
// Convert ms to seconds for Cloudflare KV expirationTtl
|
|
26
|
+
const expirationTtl = Math.floor(options.ttl / 1000);
|
|
27
|
+
await this.kv.put(key, JSON.stringify(value), { expirationTtl });
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
await this.kv.put(key, JSON.stringify(value));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async delete(key) {
|
|
34
|
+
// Cloudflare KV delete doesn't return whether key existed
|
|
35
|
+
// We check first to provide consistent interface
|
|
36
|
+
const exists = await this.kv.get(key) !== null;
|
|
37
|
+
await this.kv.delete(key);
|
|
38
|
+
return exists;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* CloudflareCryptoAdapter - Web Crypto API implementation
|
|
43
|
+
*
|
|
44
|
+
* Uses the Web Crypto API available in Cloudflare Workers.
|
|
45
|
+
*/
|
|
46
|
+
export class CloudflareCryptoAdapter {
|
|
47
|
+
async hmacSha256(data, secret) {
|
|
48
|
+
const encoder = new TextEncoder();
|
|
49
|
+
const keyData = encoder.encode(secret);
|
|
50
|
+
// Import the secret key
|
|
51
|
+
const key = await crypto.subtle.importKey('raw', keyData, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
52
|
+
// Sign the data
|
|
53
|
+
const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(data));
|
|
54
|
+
// Convert to base64
|
|
55
|
+
return btoa(String.fromCharCode(...new Uint8Array(signature)));
|
|
56
|
+
}
|
|
57
|
+
randomBytes(length) {
|
|
58
|
+
const bytes = new Uint8Array(length);
|
|
59
|
+
crypto.getRandomValues(bytes);
|
|
60
|
+
return bytes;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* ConsoleLogger - Console-based logger for Cloudflare Workers
|
|
65
|
+
*/
|
|
66
|
+
export class ConsoleLogger {
|
|
67
|
+
debug(message, ...args) {
|
|
68
|
+
console.debug(message, ...args);
|
|
69
|
+
}
|
|
70
|
+
info(message, ...args) {
|
|
71
|
+
console.info(message, ...args);
|
|
72
|
+
}
|
|
73
|
+
warn(message, ...args) {
|
|
74
|
+
console.warn(message, ...args);
|
|
75
|
+
}
|
|
76
|
+
error(message, ...args) {
|
|
77
|
+
console.error(message, ...args);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Create a RuntimeAdapter for Cloudflare Workers
|
|
82
|
+
*
|
|
83
|
+
* @param env - Cloudflare environment bindings (includes KV namespace)
|
|
84
|
+
* @param kvNamespace - Optional KV namespace name (defaults to 'KV')
|
|
85
|
+
* @returns RuntimeAdapter instance configured for Cloudflare Workers
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* import { createCloudflareAdapter } from '@firela/runtime-adapters/cloudflare'
|
|
90
|
+
*
|
|
91
|
+
* export default {
|
|
92
|
+
* async fetch(request, env, ctx) {
|
|
93
|
+
* const adapter = createCloudflareAdapter(env)
|
|
94
|
+
*
|
|
95
|
+
* // Use KV store
|
|
96
|
+
* await adapter.kv.set('session:abc', { userId: 'user1' }, { ttl: 3600000 })
|
|
97
|
+
*
|
|
98
|
+
* // Use crypto
|
|
99
|
+
* const signature = await adapter.crypto.hmacSha256('payload', 'secret')
|
|
100
|
+
*
|
|
101
|
+
* return new Response('OK')
|
|
102
|
+
* }
|
|
103
|
+
* }
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export function createCloudflareAdapter(env, kvNamespace = 'KV') {
|
|
107
|
+
// Get KV namespace from env
|
|
108
|
+
const kv = env[kvNamespace];
|
|
109
|
+
if (!kv) {
|
|
110
|
+
throw new Error(`KV namespace '${kvNamespace}' not found in environment bindings`);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
kv: new CloudflareKVStore(kv),
|
|
114
|
+
logger: new ConsoleLogger(),
|
|
115
|
+
platform: 'cloudflare',
|
|
116
|
+
crypto: new CloudflareCryptoAdapter(),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=cloudflare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../src/cloudflare.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @firela/runtime-adapters - Platform-agnostic runtime abstraction
|
|
3
|
+
*
|
|
4
|
+
* This package provides interfaces and implementations for abstracting
|
|
5
|
+
* platform-specific capabilities across different runtime environments.
|
|
6
|
+
*
|
|
7
|
+
* ## Available Adapters
|
|
8
|
+
*
|
|
9
|
+
* - **Cloudflare Workers**: `import { createCloudflareAdapter } from '@firela/runtime-adapters/cloudflare'`
|
|
10
|
+
* - **Node.js**: `import { createNodeAdapter } from '@firela/runtime-adapters/node'`
|
|
11
|
+
*
|
|
12
|
+
* ## Usage
|
|
13
|
+
*
|
|
14
|
+
* ```typescript
|
|
15
|
+
* // For Cloudflare Workers
|
|
16
|
+
* import { createCloudflareAdapter } from '@firela/runtime-adapters/cloudflare'
|
|
17
|
+
*
|
|
18
|
+
* export default {
|
|
19
|
+
* async fetch(request, env) {
|
|
20
|
+
* const adapter = createCloudflareAdapter(env)
|
|
21
|
+
* await adapter.kv.set('key', 'value')
|
|
22
|
+
* return new Response('OK')
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* // For Node.js
|
|
27
|
+
* import { createNodeAdapter } from '@firela/runtime-adapters/node'
|
|
28
|
+
*
|
|
29
|
+
* const adapter = createNodeAdapter()
|
|
30
|
+
* await adapter.kv.set('key', 'value')
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export type { KVStore, CryptoAdapter, Logger, RuntimeAdapter } from './types.js';
|
|
34
|
+
export { createCloudflareAdapter, CloudflareKVStore, CloudflareCryptoAdapter } from './cloudflare.js';
|
|
35
|
+
export { createNodeAdapter, MemoryKVStore, NodeCryptoAdapter, ConsoleLogger } from './node.js';
|
|
36
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGH,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAGhF,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AACrG,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @firela/runtime-adapters - Platform-agnostic runtime abstraction
|
|
3
|
+
*
|
|
4
|
+
* This package provides interfaces and implementations for abstracting
|
|
5
|
+
* platform-specific capabilities across different runtime environments.
|
|
6
|
+
*
|
|
7
|
+
* ## Available Adapters
|
|
8
|
+
*
|
|
9
|
+
* - **Cloudflare Workers**: `import { createCloudflareAdapter } from '@firela/runtime-adapters/cloudflare'`
|
|
10
|
+
* - **Node.js**: `import { createNodeAdapter } from '@firela/runtime-adapters/node'`
|
|
11
|
+
*
|
|
12
|
+
* ## Usage
|
|
13
|
+
*
|
|
14
|
+
* ```typescript
|
|
15
|
+
* // For Cloudflare Workers
|
|
16
|
+
* import { createCloudflareAdapter } from '@firela/runtime-adapters/cloudflare'
|
|
17
|
+
*
|
|
18
|
+
* export default {
|
|
19
|
+
* async fetch(request, env) {
|
|
20
|
+
* const adapter = createCloudflareAdapter(env)
|
|
21
|
+
* await adapter.kv.set('key', 'value')
|
|
22
|
+
* return new Response('OK')
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* // For Node.js
|
|
27
|
+
* import { createNodeAdapter } from '@firela/runtime-adapters/node'
|
|
28
|
+
*
|
|
29
|
+
* const adapter = createNodeAdapter()
|
|
30
|
+
* await adapter.kv.set('key', 'value')
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
// Re-export adapters for convenience (sub-path exports recommended)
|
|
34
|
+
export { createCloudflareAdapter, CloudflareKVStore, CloudflareCryptoAdapter } from './cloudflare.js';
|
|
35
|
+
export { createNodeAdapter, MemoryKVStore, NodeCryptoAdapter, ConsoleLogger } from './node.js';
|
|
36
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH,oEAAoE;AACpE,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AACrG,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA"}
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js Runtime Adapter
|
|
3
|
+
*
|
|
4
|
+
* Provides RuntimeAdapter implementation for Node.js environment:
|
|
5
|
+
* - In-memory Map for KVStore (MemoryKVStore)
|
|
6
|
+
* - Node.js crypto module for CryptoAdapter
|
|
7
|
+
* - Console for Logger
|
|
8
|
+
*/
|
|
9
|
+
import type { KVStore, CryptoAdapter, Logger, RuntimeAdapter } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* MemoryKVStore - In-memory KVStore implementation
|
|
12
|
+
*
|
|
13
|
+
* Stores values in a Map with optional TTL support.
|
|
14
|
+
* Note: Data is not persisted and is lost on process restart.
|
|
15
|
+
*/
|
|
16
|
+
export declare class MemoryKVStore implements KVStore {
|
|
17
|
+
private store;
|
|
18
|
+
get<T = unknown>(key: string): Promise<T | null>;
|
|
19
|
+
set<T>(key: string, value: T, options?: {
|
|
20
|
+
ttl?: number;
|
|
21
|
+
}): Promise<void>;
|
|
22
|
+
delete(key: string): Promise<boolean>;
|
|
23
|
+
/**
|
|
24
|
+
* Clear all entries (useful for testing)
|
|
25
|
+
*/
|
|
26
|
+
clear(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Get number of entries (useful for testing)
|
|
29
|
+
*/
|
|
30
|
+
get size(): number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* NodeCryptoAdapter - Node.js crypto module implementation
|
|
34
|
+
*
|
|
35
|
+
* Uses the built-in node:crypto module for cryptographic operations.
|
|
36
|
+
*/
|
|
37
|
+
export declare class NodeCryptoAdapter implements CryptoAdapter {
|
|
38
|
+
hmacSha256(data: string, secret: string): Promise<string>;
|
|
39
|
+
randomBytes(length: number): Uint8Array;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* ConsoleLogger - Console-based logger for Node.js
|
|
43
|
+
*/
|
|
44
|
+
export declare class ConsoleLogger implements Logger {
|
|
45
|
+
debug(message: string, ...args: unknown[]): void;
|
|
46
|
+
info(message: string, ...args: unknown[]): void;
|
|
47
|
+
warn(message: string, ...args: unknown[]): void;
|
|
48
|
+
error(message: string, ...args: unknown[]): void;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Create a RuntimeAdapter for Node.js
|
|
52
|
+
*
|
|
53
|
+
* @param kv - Optional KVStore instance (defaults to MemoryKVStore)
|
|
54
|
+
* @param logger - Optional Logger instance (defaults to ConsoleLogger)
|
|
55
|
+
* @returns RuntimeAdapter instance configured for Node.js
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* import { createNodeAdapter, MemoryKVStore } from '@firela/runtime-adapters/node'
|
|
60
|
+
*
|
|
61
|
+
* // Create adapter with default in-memory KV store
|
|
62
|
+
* const adapter = createNodeAdapter()
|
|
63
|
+
*
|
|
64
|
+
* // Or with custom KV store
|
|
65
|
+
* const customKV = new MemoryKVStore()
|
|
66
|
+
* const adapter = createNodeAdapter({ kv: customKV })
|
|
67
|
+
*
|
|
68
|
+
* // Use KV store
|
|
69
|
+
* await adapter.kv.set('session:abc', { userId: 'user1' }, { ttl: 3600000 })
|
|
70
|
+
*
|
|
71
|
+
* // Use crypto
|
|
72
|
+
* const signature = await adapter.crypto.hmacSha256('payload', 'secret')
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export declare function createNodeAdapter(options?: {
|
|
76
|
+
kv?: KVStore;
|
|
77
|
+
logger?: Logger;
|
|
78
|
+
}): RuntimeAdapter;
|
|
79
|
+
export type { KVStore, CryptoAdapter, Logger, RuntimeAdapter } from './types.js';
|
|
80
|
+
//# sourceMappingURL=node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhF;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,OAAO;IAC3C,OAAO,CAAC,KAAK,CAA4D;IAEnE,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAgBhD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAUxE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI3C;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,YAAW,aAAa;IAC/C,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAM/D,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU;CAGxC;AAED;;GAEG;AACH,qBAAa,aAAc,YAAW,MAAM;IAC1C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAIhD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAI/C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAI/C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;CAGjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE;IAC1C,EAAE,CAAC,EAAE,OAAO,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,GAAG,cAAc,CAOjB;AAGD,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js Runtime Adapter
|
|
3
|
+
*
|
|
4
|
+
* Provides RuntimeAdapter implementation for Node.js environment:
|
|
5
|
+
* - In-memory Map for KVStore (MemoryKVStore)
|
|
6
|
+
* - Node.js crypto module for CryptoAdapter
|
|
7
|
+
* - Console for Logger
|
|
8
|
+
*/
|
|
9
|
+
import crypto from 'node:crypto';
|
|
10
|
+
/**
|
|
11
|
+
* MemoryKVStore - In-memory KVStore implementation
|
|
12
|
+
*
|
|
13
|
+
* Stores values in a Map with optional TTL support.
|
|
14
|
+
* Note: Data is not persisted and is lost on process restart.
|
|
15
|
+
*/
|
|
16
|
+
export class MemoryKVStore {
|
|
17
|
+
store = new Map();
|
|
18
|
+
async get(key) {
|
|
19
|
+
const entry = this.store.get(key);
|
|
20
|
+
if (!entry) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
// Check TTL expiration
|
|
24
|
+
if (entry.expiresAt !== undefined && Date.now() > entry.expiresAt) {
|
|
25
|
+
this.store.delete(key);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return entry.value;
|
|
29
|
+
}
|
|
30
|
+
async set(key, value, options) {
|
|
31
|
+
const entry = { value };
|
|
32
|
+
if (options?.ttl !== undefined) {
|
|
33
|
+
entry.expiresAt = Date.now() + options.ttl;
|
|
34
|
+
}
|
|
35
|
+
this.store.set(key, entry);
|
|
36
|
+
}
|
|
37
|
+
async delete(key) {
|
|
38
|
+
return this.store.delete(key);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Clear all entries (useful for testing)
|
|
42
|
+
*/
|
|
43
|
+
clear() {
|
|
44
|
+
this.store.clear();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get number of entries (useful for testing)
|
|
48
|
+
*/
|
|
49
|
+
get size() {
|
|
50
|
+
return this.store.size;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* NodeCryptoAdapter - Node.js crypto module implementation
|
|
55
|
+
*
|
|
56
|
+
* Uses the built-in node:crypto module for cryptographic operations.
|
|
57
|
+
*/
|
|
58
|
+
export class NodeCryptoAdapter {
|
|
59
|
+
async hmacSha256(data, secret) {
|
|
60
|
+
const hmac = crypto.createHmac('sha256', secret);
|
|
61
|
+
hmac.update(data);
|
|
62
|
+
return hmac.digest('base64');
|
|
63
|
+
}
|
|
64
|
+
randomBytes(length) {
|
|
65
|
+
return crypto.randomBytes(length);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* ConsoleLogger - Console-based logger for Node.js
|
|
70
|
+
*/
|
|
71
|
+
export class ConsoleLogger {
|
|
72
|
+
debug(message, ...args) {
|
|
73
|
+
console.debug(message, ...args);
|
|
74
|
+
}
|
|
75
|
+
info(message, ...args) {
|
|
76
|
+
console.info(message, ...args);
|
|
77
|
+
}
|
|
78
|
+
warn(message, ...args) {
|
|
79
|
+
console.warn(message, ...args);
|
|
80
|
+
}
|
|
81
|
+
error(message, ...args) {
|
|
82
|
+
console.error(message, ...args);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Create a RuntimeAdapter for Node.js
|
|
87
|
+
*
|
|
88
|
+
* @param kv - Optional KVStore instance (defaults to MemoryKVStore)
|
|
89
|
+
* @param logger - Optional Logger instance (defaults to ConsoleLogger)
|
|
90
|
+
* @returns RuntimeAdapter instance configured for Node.js
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```typescript
|
|
94
|
+
* import { createNodeAdapter, MemoryKVStore } from '@firela/runtime-adapters/node'
|
|
95
|
+
*
|
|
96
|
+
* // Create adapter with default in-memory KV store
|
|
97
|
+
* const adapter = createNodeAdapter()
|
|
98
|
+
*
|
|
99
|
+
* // Or with custom KV store
|
|
100
|
+
* const customKV = new MemoryKVStore()
|
|
101
|
+
* const adapter = createNodeAdapter({ kv: customKV })
|
|
102
|
+
*
|
|
103
|
+
* // Use KV store
|
|
104
|
+
* await adapter.kv.set('session:abc', { userId: 'user1' }, { ttl: 3600000 })
|
|
105
|
+
*
|
|
106
|
+
* // Use crypto
|
|
107
|
+
* const signature = await adapter.crypto.hmacSha256('payload', 'secret')
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export function createNodeAdapter(options) {
|
|
111
|
+
return {
|
|
112
|
+
kv: options?.kv ?? new MemoryKVStore(),
|
|
113
|
+
logger: options?.logger ?? new ConsoleLogger(),
|
|
114
|
+
platform: 'node',
|
|
115
|
+
crypto: new NodeCryptoAdapter(),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=node.js.map
|
package/dist/node.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RuntimeAdapter Types - Platform-agnostic runtime abstraction
|
|
3
|
+
*
|
|
4
|
+
* This module defines interfaces for abstracting platform-specific capabilities
|
|
5
|
+
* across different runtime environments (Cloudflare Workers, Node.js, Vercel, etc.)
|
|
6
|
+
*
|
|
7
|
+
* ## Design Decisions (Expert Review 2026-03-12)
|
|
8
|
+
*
|
|
9
|
+
* 1. **RuntimeAdapter vs RuntimeContext** - These are independent interfaces:
|
|
10
|
+
* - RuntimeAdapter: Platform capabilities (kv, crypto) - for Workers
|
|
11
|
+
* - RuntimeContext: Application context (config, storage, events) - for CLI/OpenClaw
|
|
12
|
+
*
|
|
13
|
+
* 2. **No SQLDatabase interface** - Existing StorageAdapter satisfies business needs
|
|
14
|
+
*
|
|
15
|
+
* 3. **KVStore usage** - For session, rate limit counter, cache
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Logger interface - Platform-agnostic logging
|
|
19
|
+
*
|
|
20
|
+
* Implementations:
|
|
21
|
+
* - Cloudflare: console (native)
|
|
22
|
+
* - Node.js: console or pino/winston
|
|
23
|
+
*/
|
|
24
|
+
export interface Logger {
|
|
25
|
+
debug(message: string, ...args: unknown[]): void;
|
|
26
|
+
info(message: string, ...args: unknown[]): void;
|
|
27
|
+
warn(message: string, ...args: unknown[]): void;
|
|
28
|
+
error(message: string, ...args: unknown[]): void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* KVStore - Key-value storage for session, rate limiting, and caching
|
|
32
|
+
*
|
|
33
|
+
* Implementations:
|
|
34
|
+
* - Cloudflare: KVNamespace binding
|
|
35
|
+
* - Node.js: MemoryKVStore / SQLite
|
|
36
|
+
* - Vercel/Netlify: Upstash Redis
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* // Store session with TTL
|
|
41
|
+
* await kv.set('session:abc123', { userId: 'user1' }, { ttl: 3600000 }) // 1 hour
|
|
42
|
+
*
|
|
43
|
+
* // Retrieve session
|
|
44
|
+
* const session = await kv.get<{ userId: string }>('session:abc123')
|
|
45
|
+
*
|
|
46
|
+
* // Delete session
|
|
47
|
+
* await kv.delete('session:abc123')
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export interface KVStore {
|
|
51
|
+
/**
|
|
52
|
+
* Get a value by key
|
|
53
|
+
* @param key - The key to retrieve
|
|
54
|
+
* @returns The value or null if not found
|
|
55
|
+
*/
|
|
56
|
+
get<T = unknown>(key: string): Promise<T | null>;
|
|
57
|
+
/**
|
|
58
|
+
* Set a value with optional TTL
|
|
59
|
+
* @param key - The key to set
|
|
60
|
+
* @param value - The value to store
|
|
61
|
+
* @param options - Optional settings including TTL in milliseconds
|
|
62
|
+
*/
|
|
63
|
+
set<T>(key: string, value: T, options?: {
|
|
64
|
+
ttl?: number;
|
|
65
|
+
}): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Delete a key
|
|
68
|
+
* @param key - The key to delete
|
|
69
|
+
* @returns true if the key existed and was deleted
|
|
70
|
+
*/
|
|
71
|
+
delete(key: string): Promise<boolean>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* CryptoAdapter - Platform-agnostic cryptographic capabilities
|
|
75
|
+
*
|
|
76
|
+
* Implementations:
|
|
77
|
+
* - Cloudflare: Web Crypto API
|
|
78
|
+
* - Node.js: node:crypto module
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* // HMAC for webhook signature verification
|
|
83
|
+
* const signature = await crypto.hmacSha256(payload, secret)
|
|
84
|
+
*
|
|
85
|
+
* // Generate random bytes for session IDs
|
|
86
|
+
* const sessionId = crypto.randomBytes(16)
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export interface CryptoAdapter {
|
|
90
|
+
/**
|
|
91
|
+
* Compute HMAC-SHA256
|
|
92
|
+
* @param data - The data to sign
|
|
93
|
+
* @param secret - The secret key
|
|
94
|
+
* @returns Base64-encoded signature
|
|
95
|
+
*/
|
|
96
|
+
hmacSha256(data: string, secret: string): Promise<string>;
|
|
97
|
+
/**
|
|
98
|
+
* Generate cryptographically secure random bytes
|
|
99
|
+
* @param length - Number of bytes to generate
|
|
100
|
+
* @returns Uint8Array of random bytes
|
|
101
|
+
*/
|
|
102
|
+
randomBytes(length: number): Uint8Array;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* RuntimeAdapter - Platform abstraction for Worker environments
|
|
106
|
+
*
|
|
107
|
+
* This interface abstracts platform-specific capabilities for Cloudflare Workers,
|
|
108
|
+
* Vercel Edge Functions, Netlify Edge Functions, and similar edge runtimes.
|
|
109
|
+
*
|
|
110
|
+
* Note: CLI/OpenClaw continue using RuntimeContext (includes config, platform.openUrl).
|
|
111
|
+
* The two interfaces are independent and serve different purposes:
|
|
112
|
+
* - RuntimeAdapter: Platform capabilities (kv, crypto)
|
|
113
|
+
* - RuntimeContext: Application context (config, storage, events)
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```typescript
|
|
117
|
+
* // In Cloudflare Worker
|
|
118
|
+
* import { createCloudflareAdapter } from '@firela/runtime-adapters/cloudflare'
|
|
119
|
+
*
|
|
120
|
+
* export default {
|
|
121
|
+
* async fetch(request, env) {
|
|
122
|
+
* const adapter = createCloudflareAdapter(env)
|
|
123
|
+
* await adapter.kv.set('key', 'value')
|
|
124
|
+
* return new Response('OK')
|
|
125
|
+
* }
|
|
126
|
+
* }
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
export interface RuntimeAdapter {
|
|
130
|
+
/** Key-value store for session, rate limiting, and cache */
|
|
131
|
+
readonly kv: KVStore;
|
|
132
|
+
/** Logger instance */
|
|
133
|
+
readonly logger: Logger;
|
|
134
|
+
/** Platform identifier */
|
|
135
|
+
readonly platform: 'cloudflare' | 'node' | 'vercel' | 'netlify';
|
|
136
|
+
/** Cryptographic utilities */
|
|
137
|
+
readonly crypto: CryptoAdapter;
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;GAMG;AACH,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;IAC/C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;IAC/C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;CACjD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,OAAO;IACtB;;;;OAIG;IACH,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;IAEhD;;;;;OAKG;IACH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAExE;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACtC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAEzD;;;;OAIG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAA;CACxC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAA;IAEpB,sBAAsB;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IAEvB,0BAA0B;IAC1B,QAAQ,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IAE/D,8BAA8B;IAC9B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;CAC/B"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RuntimeAdapter Types - Platform-agnostic runtime abstraction
|
|
3
|
+
*
|
|
4
|
+
* This module defines interfaces for abstracting platform-specific capabilities
|
|
5
|
+
* across different runtime environments (Cloudflare Workers, Node.js, Vercel, etc.)
|
|
6
|
+
*
|
|
7
|
+
* ## Design Decisions (Expert Review 2026-03-12)
|
|
8
|
+
*
|
|
9
|
+
* 1. **RuntimeAdapter vs RuntimeContext** - These are independent interfaces:
|
|
10
|
+
* - RuntimeAdapter: Platform capabilities (kv, crypto) - for Workers
|
|
11
|
+
* - RuntimeContext: Application context (config, storage, events) - for CLI/OpenClaw
|
|
12
|
+
*
|
|
13
|
+
* 2. **No SQLDatabase interface** - Existing StorageAdapter satisfies business needs
|
|
14
|
+
*
|
|
15
|
+
* 3. **KVStore usage** - For session, rate limit counter, cache
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@firela/runtime-adapters",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Platform-agnostic runtime adapters for Cloudflare Workers, Node.js, and edge runtimes",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./cloudflare": {
|
|
15
|
+
"types": "./dist/cloudflare.d.ts",
|
|
16
|
+
"import": "./dist/cloudflare.js",
|
|
17
|
+
"default": "./dist/cloudflare.js"
|
|
18
|
+
},
|
|
19
|
+
"./node": {
|
|
20
|
+
"types": "./dist/node.d.ts",
|
|
21
|
+
"import": "./dist/node.js",
|
|
22
|
+
"default": "./dist/node.js"
|
|
23
|
+
},
|
|
24
|
+
"./types": {
|
|
25
|
+
"types": "./dist/types.d.ts",
|
|
26
|
+
"import": "./dist/types.js",
|
|
27
|
+
"default": "./dist/types.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist"
|
|
32
|
+
],
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"runtime",
|
|
38
|
+
"adapters",
|
|
39
|
+
"cloudflare-workers",
|
|
40
|
+
"nodejs",
|
|
41
|
+
"edge-computing",
|
|
42
|
+
"platform-agnostic"
|
|
43
|
+
],
|
|
44
|
+
"author": "fire-la",
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/fire-la/billclaw.git",
|
|
49
|
+
"directory": "packages/runtime-adapters"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@cloudflare/workers-types": "^4.20260312.0",
|
|
53
|
+
"@types/node": "^25.2.0",
|
|
54
|
+
"oxfmt": "^0.1.0",
|
|
55
|
+
"oxlint": "^0.15.0",
|
|
56
|
+
"typescript": "^5.8.0",
|
|
57
|
+
"vitest": "^2.1.0"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=20.0.0"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "tsc",
|
|
64
|
+
"dev": "tsc --watch",
|
|
65
|
+
"lint": "oxlint",
|
|
66
|
+
"format": "oxfmt src/",
|
|
67
|
+
"format:write": "oxfmt -w src/",
|
|
68
|
+
"clean": "rm -rf dist"
|
|
69
|
+
}
|
|
70
|
+
}
|