@velajs/cloudflare 1.8.0 → 1.9.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/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,10 @@ export { AIService } from './services/ai.service';
|
|
|
23
23
|
export { VectorizeService } from './services/vectorize.service';
|
|
24
24
|
export { HyperdriveService } from './services/hyperdrive.service';
|
|
25
25
|
export { EnvService } from './services/env.service';
|
|
26
|
+
export { FlagshipFlagDriver, flagshipFlagDriver } from './services/flagship-flag.driver';
|
|
27
|
+
export type { FlagshipBinding, FlagshipFlagDriverOptions } from './services/flagship-flag.driver';
|
|
28
|
+
export { KvFlagDriver, kvFlagDriver } from './services/kv-flag.driver';
|
|
29
|
+
export type { KvFlagDriverOptions } from './services/kv-flag.driver';
|
|
26
30
|
export { Env } from './decorators/env';
|
|
27
31
|
export { Scheduled } from './decorators/scheduled';
|
|
28
32
|
export { QueueConsumer } from './decorators/queue-consumer';
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,9 @@ export { AIService } from "./services/ai.service.js";
|
|
|
24
24
|
export { VectorizeService } from "./services/vectorize.service.js";
|
|
25
25
|
export { HyperdriveService } from "./services/hyperdrive.service.js";
|
|
26
26
|
export { EnvService } from "./services/env.service.js";
|
|
27
|
+
// Feature-flag drivers (implement @velajs/feature-flags' FeatureFlagDriver contract)
|
|
28
|
+
export { FlagshipFlagDriver, flagshipFlagDriver } from "./services/flagship-flag.driver.js";
|
|
29
|
+
export { KvFlagDriver, kvFlagDriver } from "./services/kv-flag.driver.js";
|
|
27
30
|
// Decorators
|
|
28
31
|
export { Env } from "./decorators/env.js";
|
|
29
32
|
export { Scheduled } from "./decorators/scheduled.js";
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { FeatureFlagDriver, FlagContext } from '@velajs/feature-flags';
|
|
2
|
+
/**
|
|
3
|
+
* The subset of a Cloudflare **Flagship** binding this driver evaluates against
|
|
4
|
+
* — the four typed value methods. The binding itself returns the supplied
|
|
5
|
+
* `defaultValue` on evaluation errors; transport-level failures reject and are
|
|
6
|
+
* left to propagate (never-throw is the service layer's job, not the driver's).
|
|
7
|
+
*
|
|
8
|
+
* @see https://developers.cloudflare.com/flagship/binding/
|
|
9
|
+
*/
|
|
10
|
+
export interface FlagshipBinding {
|
|
11
|
+
getBooleanValue(key: string, defaultValue: boolean, context?: FlagContext): Promise<boolean>;
|
|
12
|
+
getStringValue(key: string, defaultValue: string, context?: FlagContext): Promise<string>;
|
|
13
|
+
getNumberValue(key: string, defaultValue: number, context?: FlagContext): Promise<number>;
|
|
14
|
+
getObjectValue<T extends object>(key: string, defaultValue: T, context?: FlagContext): Promise<T>;
|
|
15
|
+
}
|
|
16
|
+
export interface FlagshipFlagDriverOptions {
|
|
17
|
+
/** Driver name used for `use(name)` / default-driver selection. Default `"flagship"`. */
|
|
18
|
+
name?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* {@link FeatureFlagDriver} backed by a Cloudflare Flagship binding.
|
|
22
|
+
*
|
|
23
|
+
* A thin, honest wrapper: each contract method maps 1:1 onto the binding's
|
|
24
|
+
* corresponding value method, forwarding the caller's `fallback` (the binding's
|
|
25
|
+
* `defaultValue`) and evaluation context. The binding resolves the fallback on
|
|
26
|
+
* evaluation errors; anything the binding *rejects* with (e.g. a `remote: true`
|
|
27
|
+
* dev-proxy tunnel dropping) propagates — `@velajs/feature-flags`'s service owns
|
|
28
|
+
* the never-throw guarantee.
|
|
29
|
+
*
|
|
30
|
+
* The binding only exists per request in a Worker, so pass a lazy accessor when
|
|
31
|
+
* wiring from a bootstrap factory (mirroring how {@link EnvService} reads are
|
|
32
|
+
* deferred); a resolved binding may be passed directly in tests.
|
|
33
|
+
*
|
|
34
|
+
* ```ts
|
|
35
|
+
* FeatureFlagsModule.forRootAsync({
|
|
36
|
+
* inject: [EnvService],
|
|
37
|
+
* useFactory: (env: EnvService) => ({
|
|
38
|
+
* drivers: [flagshipFlagDriver(() => env.get<FlagshipBinding>('FLAGS')!)],
|
|
39
|
+
* }),
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* Evaluation ergonomics (the 1:1 binding-method mapping) are ported from the
|
|
44
|
+
* Stratal feature-flags service (MIT, © Temitayo Fadojutimi), reshaped as a
|
|
45
|
+
* bare driver.
|
|
46
|
+
*/
|
|
47
|
+
export declare class FlagshipFlagDriver implements FeatureFlagDriver {
|
|
48
|
+
readonly name: string;
|
|
49
|
+
private readonly resolve;
|
|
50
|
+
constructor(binding: FlagshipBinding | (() => FlagshipBinding), options?: FlagshipFlagDriverOptions);
|
|
51
|
+
getBoolean(key: string, fallback: boolean, ctx?: FlagContext): Promise<boolean>;
|
|
52
|
+
getString(key: string, fallback: string, ctx?: FlagContext): Promise<string>;
|
|
53
|
+
getNumber(key: string, fallback: number, ctx?: FlagContext): Promise<number>;
|
|
54
|
+
getObject<T extends object>(key: string, fallback: T, ctx?: FlagContext): Promise<T>;
|
|
55
|
+
}
|
|
56
|
+
/** Convenience factory for {@link FlagshipFlagDriver}. */
|
|
57
|
+
export declare function flagshipFlagDriver(binding: FlagshipBinding | (() => FlagshipBinding), options?: FlagshipFlagDriverOptions): FlagshipFlagDriver;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {@link FeatureFlagDriver} backed by a Cloudflare Flagship binding.
|
|
3
|
+
*
|
|
4
|
+
* A thin, honest wrapper: each contract method maps 1:1 onto the binding's
|
|
5
|
+
* corresponding value method, forwarding the caller's `fallback` (the binding's
|
|
6
|
+
* `defaultValue`) and evaluation context. The binding resolves the fallback on
|
|
7
|
+
* evaluation errors; anything the binding *rejects* with (e.g. a `remote: true`
|
|
8
|
+
* dev-proxy tunnel dropping) propagates — `@velajs/feature-flags`'s service owns
|
|
9
|
+
* the never-throw guarantee.
|
|
10
|
+
*
|
|
11
|
+
* The binding only exists per request in a Worker, so pass a lazy accessor when
|
|
12
|
+
* wiring from a bootstrap factory (mirroring how {@link EnvService} reads are
|
|
13
|
+
* deferred); a resolved binding may be passed directly in tests.
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* FeatureFlagsModule.forRootAsync({
|
|
17
|
+
* inject: [EnvService],
|
|
18
|
+
* useFactory: (env: EnvService) => ({
|
|
19
|
+
* drivers: [flagshipFlagDriver(() => env.get<FlagshipBinding>('FLAGS')!)],
|
|
20
|
+
* }),
|
|
21
|
+
* });
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* Evaluation ergonomics (the 1:1 binding-method mapping) are ported from the
|
|
25
|
+
* Stratal feature-flags service (MIT, © Temitayo Fadojutimi), reshaped as a
|
|
26
|
+
* bare driver.
|
|
27
|
+
*/ export class FlagshipFlagDriver {
|
|
28
|
+
name;
|
|
29
|
+
resolve;
|
|
30
|
+
constructor(binding, options = {}){
|
|
31
|
+
this.resolve = typeof binding === 'function' ? binding : ()=>binding;
|
|
32
|
+
this.name = options.name ?? 'flagship';
|
|
33
|
+
}
|
|
34
|
+
getBoolean(key, fallback, ctx) {
|
|
35
|
+
return this.resolve().getBooleanValue(key, fallback, ctx);
|
|
36
|
+
}
|
|
37
|
+
getString(key, fallback, ctx) {
|
|
38
|
+
return this.resolve().getStringValue(key, fallback, ctx);
|
|
39
|
+
}
|
|
40
|
+
getNumber(key, fallback, ctx) {
|
|
41
|
+
return this.resolve().getNumberValue(key, fallback, ctx);
|
|
42
|
+
}
|
|
43
|
+
getObject(key, fallback, ctx) {
|
|
44
|
+
return this.resolve().getObjectValue(key, fallback, ctx);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Convenience factory for {@link FlagshipFlagDriver}. */ export function flagshipFlagDriver(binding, options) {
|
|
48
|
+
return new FlagshipFlagDriver(binding, options);
|
|
49
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { FeatureFlagDriver, FlagContext } from '@velajs/feature-flags';
|
|
2
|
+
import { KVService } from './kv.service';
|
|
3
|
+
export interface KvFlagDriverOptions {
|
|
4
|
+
/** Driver name used for `use(name)` / default-driver selection. Default `"kv"`. */
|
|
5
|
+
name?: string;
|
|
6
|
+
/** Prefix prepended to every flag key before the KV read. Default `""` (none). */
|
|
7
|
+
prefix?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Cloudflare KV-backed {@link FeatureFlagDriver}. Flags are stored as JSON
|
|
11
|
+
* values under an optional key prefix and read with `get(key, 'json')`. Reads
|
|
12
|
+
* are type-checked against the requested type: a missing key or a value of the
|
|
13
|
+
* wrong JSON type returns the caller's `fallback`. KV has no targeting, so the
|
|
14
|
+
* evaluation context is ignored.
|
|
15
|
+
*
|
|
16
|
+
* The driver stays honest — it does **not** swallow errors. A KV failure (or a
|
|
17
|
+
* `SyntaxError` from a malformed stored value) propagates; the never-throw
|
|
18
|
+
* guarantee lives in `@velajs/feature-flags`'s service layer.
|
|
19
|
+
*
|
|
20
|
+
* Placed like {@link KVCacheStore}: construct it in a wiring factory over a
|
|
21
|
+
* resolved {@link KVService}.
|
|
22
|
+
*
|
|
23
|
+
* ```ts
|
|
24
|
+
* FeatureFlagsModule.forRootAsync({
|
|
25
|
+
* inject: [KVService],
|
|
26
|
+
* useFactory: (kv: KVService) => ({ drivers: [new KvFlagDriver(kv, { prefix: 'flag:' })] }),
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export declare class KvFlagDriver implements FeatureFlagDriver {
|
|
31
|
+
private readonly kv;
|
|
32
|
+
readonly name: string;
|
|
33
|
+
private readonly prefix;
|
|
34
|
+
constructor(kv: KVService, options?: KvFlagDriverOptions);
|
|
35
|
+
private get ns();
|
|
36
|
+
getBoolean(key: string, fallback: boolean, _ctx?: FlagContext): Promise<boolean>;
|
|
37
|
+
getString(key: string, fallback: string, _ctx?: FlagContext): Promise<string>;
|
|
38
|
+
getNumber(key: string, fallback: number, _ctx?: FlagContext): Promise<number>;
|
|
39
|
+
getObject<T extends object>(key: string, fallback: T, _ctx?: FlagContext): Promise<T>;
|
|
40
|
+
/**
|
|
41
|
+
* Reads and JSON-parses the (prefixed) key, returning the parsed value only
|
|
42
|
+
* when `matches` accepts its type; otherwise the caller's fallback. A missing
|
|
43
|
+
* key reads as `null` → fallback. Read/parse errors are left to propagate.
|
|
44
|
+
*/
|
|
45
|
+
private read;
|
|
46
|
+
}
|
|
47
|
+
/** Convenience factory for {@link KvFlagDriver}. */
|
|
48
|
+
export declare function kvFlagDriver(kv: KVService, options?: KvFlagDriverOptions): KvFlagDriver;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
}
|
|
7
|
+
function _ts_metadata(k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
}
|
|
10
|
+
function _ts_param(paramIndex, decorator) {
|
|
11
|
+
return function(target, key) {
|
|
12
|
+
decorator(target, key, paramIndex);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
import { Inject, Injectable } from "@velajs/vela";
|
|
16
|
+
import { KVService } from "./kv.service.js";
|
|
17
|
+
export class KvFlagDriver {
|
|
18
|
+
kv;
|
|
19
|
+
name;
|
|
20
|
+
prefix;
|
|
21
|
+
constructor(kv, options = {}){
|
|
22
|
+
this.kv = kv;
|
|
23
|
+
this.name = options.name ?? 'kv';
|
|
24
|
+
this.prefix = options.prefix ?? '';
|
|
25
|
+
}
|
|
26
|
+
get ns() {
|
|
27
|
+
return this.kv.namespace;
|
|
28
|
+
}
|
|
29
|
+
getBoolean(key, fallback, _ctx) {
|
|
30
|
+
return this.read(key, fallback, (v)=>typeof v === 'boolean');
|
|
31
|
+
}
|
|
32
|
+
getString(key, fallback, _ctx) {
|
|
33
|
+
return this.read(key, fallback, (v)=>typeof v === 'string');
|
|
34
|
+
}
|
|
35
|
+
getNumber(key, fallback, _ctx) {
|
|
36
|
+
return this.read(key, fallback, (v)=>typeof v === 'number');
|
|
37
|
+
}
|
|
38
|
+
getObject(key, fallback, _ctx) {
|
|
39
|
+
return this.read(key, fallback, (v)=>typeof v === 'object' && v !== null);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Reads and JSON-parses the (prefixed) key, returning the parsed value only
|
|
43
|
+
* when `matches` accepts its type; otherwise the caller's fallback. A missing
|
|
44
|
+
* key reads as `null` → fallback. Read/parse errors are left to propagate.
|
|
45
|
+
*/ async read(key, fallback, matches) {
|
|
46
|
+
const value = await this.ns.get(this.prefix + key, 'json');
|
|
47
|
+
if (value === null || value === undefined) return fallback;
|
|
48
|
+
return matches(value) ? value : fallback;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
KvFlagDriver = _ts_decorate([
|
|
52
|
+
Injectable(),
|
|
53
|
+
_ts_param(0, Inject(KVService)),
|
|
54
|
+
_ts_metadata("design:type", Function),
|
|
55
|
+
_ts_metadata("design:paramtypes", [
|
|
56
|
+
typeof KVService === "undefined" ? Object : KVService,
|
|
57
|
+
typeof KvFlagDriverOptions === "undefined" ? Object : KvFlagDriverOptions
|
|
58
|
+
])
|
|
59
|
+
], KvFlagDriver);
|
|
60
|
+
/** Convenience factory for {@link KvFlagDriver}. */ export function kvFlagDriver(kv, options) {
|
|
61
|
+
return new KvFlagDriver(kv, options);
|
|
62
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velajs/cloudflare",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Cloudflare Workers integration for Vela framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -45,13 +45,20 @@
|
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@cloudflare/workers-types": ">=4",
|
|
48
|
+
"@velajs/feature-flags": "^0.1.0",
|
|
48
49
|
"@velajs/vela": ">=1.11.0",
|
|
49
50
|
"hono": ">=4"
|
|
50
51
|
},
|
|
52
|
+
"peerDependenciesMeta": {
|
|
53
|
+
"@velajs/feature-flags": {
|
|
54
|
+
"optional": true
|
|
55
|
+
}
|
|
56
|
+
},
|
|
51
57
|
"devDependencies": {
|
|
52
58
|
"@cloudflare/workers-types": "^4.20260624.1",
|
|
53
59
|
"@swc/cli": "^0.8.1",
|
|
54
60
|
"@swc/core": "^1.15.43",
|
|
61
|
+
"@velajs/feature-flags": "^0.1.0",
|
|
55
62
|
"@velajs/vela": "^1.12.0",
|
|
56
63
|
"hono": "^4.12.27",
|
|
57
64
|
"typescript": "^6.0.3",
|