@flagify/astro 1.0.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/README.md +127 -0
- package/dist/adapter.cjs +57 -0
- package/dist/adapter.d.cts +31 -0
- package/dist/adapter.d.ts +31 -0
- package/dist/adapter.js +25 -0
- package/dist/chunk-XPJW7PJL.js +28 -0
- package/dist/index.cjs +111 -0
- package/dist/index.d.cts +97 -0
- package/dist/index.d.ts +97 -0
- package/dist/index.js +64 -0
- package/dist/middleware.cjs +67 -0
- package/dist/middleware.d.cts +15 -0
- package/dist/middleware.d.ts +15 -0
- package/dist/middleware.js +30 -0
- package/dist/toolbar/app.cjs +85 -0
- package/dist/toolbar/app.d.cts +7 -0
- package/dist/toolbar/app.d.ts +7 -0
- package/dist/toolbar/app.js +64 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# @flagify/astro
|
|
2
|
+
|
|
3
|
+
Official [Flagify](https://flagify.dev) integration for [Astro](https://astro.build). Evaluate feature flags in Astro pages and components with a dev toolbar for overrides.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @flagify/astro @flagify/node
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @flagify/astro @flagify/node
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Setup
|
|
14
|
+
|
|
15
|
+
### 1. Add environment variables
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# .env
|
|
19
|
+
FLAGIFY_PROJECT_KEY=your-project-key
|
|
20
|
+
FLAGIFY_PUBLIC_KEY=pk_dev_xxx
|
|
21
|
+
FLAGIFY_SECRET_KEY=sk_dev_xxx # optional, for SSR evaluation
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 2. Add the integration
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
// astro.config.mjs
|
|
28
|
+
import { defineConfig } from 'astro/config';
|
|
29
|
+
import flagify from '@flagify/astro';
|
|
30
|
+
|
|
31
|
+
export default defineConfig({
|
|
32
|
+
integrations: [flagify()],
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
This automatically registers:
|
|
37
|
+
- **Middleware** that initializes the Flagify client and parses override cookies
|
|
38
|
+
- **Dev toolbar app** for toggling flag overrides during development
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
### Define flags
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
// src/flags.ts
|
|
46
|
+
import { defineFlag } from '@flagify/astro';
|
|
47
|
+
|
|
48
|
+
export const newCheckout = defineFlag({
|
|
49
|
+
key: 'new-checkout-flow',
|
|
50
|
+
description: 'New checkout flow experience',
|
|
51
|
+
default: false,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export const heroVariant = defineFlag({
|
|
55
|
+
key: 'hero-variant',
|
|
56
|
+
description: 'A/B test for hero section',
|
|
57
|
+
default: 'control',
|
|
58
|
+
options: [
|
|
59
|
+
{ value: 'control', label: 'Control' },
|
|
60
|
+
{ value: 'variant-a', label: 'Variant A' },
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Evaluate in pages/components
|
|
66
|
+
|
|
67
|
+
```astro
|
|
68
|
+
---
|
|
69
|
+
// src/pages/index.astro
|
|
70
|
+
import { newCheckout, heroVariant } from '../flags';
|
|
71
|
+
|
|
72
|
+
const isNewCheckout = await newCheckout(Astro);
|
|
73
|
+
const hero = await heroVariant(Astro);
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
{isNewCheckout && <NewCheckoutBanner />}
|
|
77
|
+
<Hero variant={hero} />
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Flag evaluation priority
|
|
81
|
+
|
|
82
|
+
1. **Override cookie** — dev overrides from the toolbar take highest priority
|
|
83
|
+
2. **Flagify SDK** — evaluated via `@flagify/node` (cached locally)
|
|
84
|
+
3. **Default value** — the `default` from `defineFlag()`
|
|
85
|
+
|
|
86
|
+
## Dev Toolbar
|
|
87
|
+
|
|
88
|
+
In development mode, the Flagify toolbar app lets you set flag overrides as JSON. Overrides are stored in a `flagify-overrides` cookie and persist across page navigations.
|
|
89
|
+
|
|
90
|
+
## Vercel Flags SDK Adapter
|
|
91
|
+
|
|
92
|
+
For projects using the [Vercel Flags SDK](https://flags-sdk.dev):
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { createFlagifyAdapter } from '@flagify/astro/adapter';
|
|
96
|
+
|
|
97
|
+
const { adapter } = createFlagifyAdapter();
|
|
98
|
+
|
|
99
|
+
export const checkout = flag({
|
|
100
|
+
key: 'new-checkout-flow',
|
|
101
|
+
adapter: adapter('new-checkout-flow'),
|
|
102
|
+
defaultValue: false,
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## SSG Limitations
|
|
107
|
+
|
|
108
|
+
In static builds (SSG), flags are evaluated at **build time** without user context. This works for global flags (kill switches, percentage rollouts) but not for user-targeted flags. For user-targeted evaluation, use SSR mode.
|
|
109
|
+
|
|
110
|
+
## TypeScript
|
|
111
|
+
|
|
112
|
+
Add type safety for `context.locals`:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
// src/env.d.ts
|
|
116
|
+
/// <reference types="astro/client" />
|
|
117
|
+
|
|
118
|
+
declare namespace App {
|
|
119
|
+
interface Locals {
|
|
120
|
+
flagifyOverrides: Record<string, unknown>;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT
|
package/dist/adapter.cjs
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/adapter.ts
|
|
21
|
+
var adapter_exports = {};
|
|
22
|
+
__export(adapter_exports, {
|
|
23
|
+
createFlagifyAdapter: () => createFlagifyAdapter
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(adapter_exports);
|
|
26
|
+
|
|
27
|
+
// src/client.ts
|
|
28
|
+
var import_node = require("@flagify/node");
|
|
29
|
+
var client = null;
|
|
30
|
+
var clientReady = null;
|
|
31
|
+
async function waitForClient() {
|
|
32
|
+
if (clientReady) await clientReady;
|
|
33
|
+
return client;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/adapter.ts
|
|
37
|
+
function createFlagifyAdapter(options) {
|
|
38
|
+
const baseOrigin = options?.origin ?? "https://app.flagify.dev";
|
|
39
|
+
return {
|
|
40
|
+
adapter: (key) => ({
|
|
41
|
+
decide: async ({ entities, defaultValue }) => {
|
|
42
|
+
const client2 = await waitForClient();
|
|
43
|
+
if (!client2) return defaultValue;
|
|
44
|
+
if (entities) {
|
|
45
|
+
const result = await client2.evaluate(key, entities);
|
|
46
|
+
return result.value;
|
|
47
|
+
}
|
|
48
|
+
return client2.getValue(key, defaultValue);
|
|
49
|
+
},
|
|
50
|
+
origin: `${baseOrigin}/flags/${key}`
|
|
51
|
+
})
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
55
|
+
0 && (module.exports = {
|
|
56
|
+
createFlagifyAdapter
|
|
57
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { FlagifyUser } from '@flagify/node';
|
|
2
|
+
|
|
3
|
+
interface FlagAdapter<T, E> {
|
|
4
|
+
decide: (params: {
|
|
5
|
+
entities?: E;
|
|
6
|
+
defaultValue?: T;
|
|
7
|
+
}) => Promise<T>;
|
|
8
|
+
origin?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Creates a Flagify adapter compatible with the Vercel Flags SDK.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { createFlagifyAdapter } from '@flagify/astro/adapter';
|
|
15
|
+
*
|
|
16
|
+
* const { adapter } = createFlagifyAdapter();
|
|
17
|
+
*
|
|
18
|
+
* export const newCheckout = flag({
|
|
19
|
+
* key: 'new-checkout-flow',
|
|
20
|
+
* adapter: adapter('new-checkout-flow'),
|
|
21
|
+
* defaultValue: false,
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare function createFlagifyAdapter(options?: {
|
|
26
|
+
origin?: string;
|
|
27
|
+
}): {
|
|
28
|
+
adapter: <T>(key: string) => FlagAdapter<T, FlagifyUser>;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export { createFlagifyAdapter };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { FlagifyUser } from '@flagify/node';
|
|
2
|
+
|
|
3
|
+
interface FlagAdapter<T, E> {
|
|
4
|
+
decide: (params: {
|
|
5
|
+
entities?: E;
|
|
6
|
+
defaultValue?: T;
|
|
7
|
+
}) => Promise<T>;
|
|
8
|
+
origin?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Creates a Flagify adapter compatible with the Vercel Flags SDK.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { createFlagifyAdapter } from '@flagify/astro/adapter';
|
|
15
|
+
*
|
|
16
|
+
* const { adapter } = createFlagifyAdapter();
|
|
17
|
+
*
|
|
18
|
+
* export const newCheckout = flag({
|
|
19
|
+
* key: 'new-checkout-flow',
|
|
20
|
+
* adapter: adapter('new-checkout-flow'),
|
|
21
|
+
* defaultValue: false,
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare function createFlagifyAdapter(options?: {
|
|
26
|
+
origin?: string;
|
|
27
|
+
}): {
|
|
28
|
+
adapter: <T>(key: string) => FlagAdapter<T, FlagifyUser>;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export { createFlagifyAdapter };
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
waitForClient
|
|
3
|
+
} from "./chunk-XPJW7PJL.js";
|
|
4
|
+
|
|
5
|
+
// src/adapter.ts
|
|
6
|
+
function createFlagifyAdapter(options) {
|
|
7
|
+
const baseOrigin = options?.origin ?? "https://app.flagify.dev";
|
|
8
|
+
return {
|
|
9
|
+
adapter: (key) => ({
|
|
10
|
+
decide: async ({ entities, defaultValue }) => {
|
|
11
|
+
const client = await waitForClient();
|
|
12
|
+
if (!client) return defaultValue;
|
|
13
|
+
if (entities) {
|
|
14
|
+
const result = await client.evaluate(key, entities);
|
|
15
|
+
return result.value;
|
|
16
|
+
}
|
|
17
|
+
return client.getValue(key, defaultValue);
|
|
18
|
+
},
|
|
19
|
+
origin: `${baseOrigin}/flags/${key}`
|
|
20
|
+
})
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
createFlagifyAdapter
|
|
25
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
import { Flagify } from "@flagify/node";
|
|
3
|
+
var client = null;
|
|
4
|
+
var clientReady = null;
|
|
5
|
+
function initClient(config) {
|
|
6
|
+
if (client) return;
|
|
7
|
+
client = new Flagify(config);
|
|
8
|
+
clientReady = client.ready();
|
|
9
|
+
}
|
|
10
|
+
function getClient() {
|
|
11
|
+
return client;
|
|
12
|
+
}
|
|
13
|
+
async function waitForClient() {
|
|
14
|
+
if (clientReady) await clientReady;
|
|
15
|
+
return client;
|
|
16
|
+
}
|
|
17
|
+
function destroyClient() {
|
|
18
|
+
client?.destroy();
|
|
19
|
+
client = null;
|
|
20
|
+
clientReady = null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
initClient,
|
|
25
|
+
getClient,
|
|
26
|
+
waitForClient,
|
|
27
|
+
destroyClient
|
|
28
|
+
};
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
default: () => flagifyIntegration,
|
|
24
|
+
defineFlag: () => defineFlag,
|
|
25
|
+
destroyClient: () => destroyClient,
|
|
26
|
+
getClient: () => getClient,
|
|
27
|
+
initClient: () => initClient,
|
|
28
|
+
waitForClient: () => waitForClient
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(index_exports);
|
|
31
|
+
|
|
32
|
+
// src/integration.ts
|
|
33
|
+
var import_meta = {};
|
|
34
|
+
var FLAG_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/><line x1="4" y1="22" x2="4" y2="15"/></svg>`;
|
|
35
|
+
function flagifyIntegration(_options) {
|
|
36
|
+
return {
|
|
37
|
+
name: "@flagify/astro",
|
|
38
|
+
hooks: {
|
|
39
|
+
"astro:config:setup": ({
|
|
40
|
+
addMiddleware,
|
|
41
|
+
addDevToolbarApp
|
|
42
|
+
}) => {
|
|
43
|
+
addMiddleware({
|
|
44
|
+
entrypoint: "@flagify/astro/middleware",
|
|
45
|
+
order: "pre"
|
|
46
|
+
});
|
|
47
|
+
addDevToolbarApp({
|
|
48
|
+
id: "flagify-flags",
|
|
49
|
+
name: "Feature Flags",
|
|
50
|
+
icon: FLAG_ICON,
|
|
51
|
+
entrypoint: new URL("./toolbar/app.js", import_meta.url)
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/client.ts
|
|
59
|
+
var import_node = require("@flagify/node");
|
|
60
|
+
var client = null;
|
|
61
|
+
var clientReady = null;
|
|
62
|
+
function initClient(config) {
|
|
63
|
+
if (client) return;
|
|
64
|
+
client = new import_node.Flagify(config);
|
|
65
|
+
clientReady = client.ready();
|
|
66
|
+
}
|
|
67
|
+
function getClient() {
|
|
68
|
+
return client;
|
|
69
|
+
}
|
|
70
|
+
async function waitForClient() {
|
|
71
|
+
if (clientReady) await clientReady;
|
|
72
|
+
return client;
|
|
73
|
+
}
|
|
74
|
+
function destroyClient() {
|
|
75
|
+
client?.destroy();
|
|
76
|
+
client = null;
|
|
77
|
+
clientReady = null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/flag.ts
|
|
81
|
+
var OVERRIDE_COOKIE = "flagify-overrides";
|
|
82
|
+
function defineFlag(definition) {
|
|
83
|
+
const evaluate = async (astro) => {
|
|
84
|
+
const overrideCookie = astro.cookies.get(OVERRIDE_COOKIE);
|
|
85
|
+
if (overrideCookie) {
|
|
86
|
+
try {
|
|
87
|
+
const overrides = JSON.parse(overrideCookie.value);
|
|
88
|
+
if (definition.key in overrides) {
|
|
89
|
+
return overrides[definition.key];
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const client2 = getClient();
|
|
95
|
+
if (!client2) return definition.default;
|
|
96
|
+
if (typeof definition.default === "boolean") {
|
|
97
|
+
return client2.isEnabled(definition.key);
|
|
98
|
+
}
|
|
99
|
+
return client2.getValue(definition.key, definition.default);
|
|
100
|
+
};
|
|
101
|
+
evaluate._definition = definition;
|
|
102
|
+
return evaluate;
|
|
103
|
+
}
|
|
104
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
105
|
+
0 && (module.exports = {
|
|
106
|
+
defineFlag,
|
|
107
|
+
destroyClient,
|
|
108
|
+
getClient,
|
|
109
|
+
initClient,
|
|
110
|
+
waitForClient
|
|
111
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { FlagifyOptions, FlagifyUser, Flagify } from '@flagify/node';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Options passed to the flagify() integration in astro.config.mjs.
|
|
5
|
+
*/
|
|
6
|
+
interface FlagifyAstroOptions extends FlagifyOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Function to extract user context from the request.
|
|
9
|
+
* Called by middleware on each request.
|
|
10
|
+
* If not provided, no user context is set.
|
|
11
|
+
*/
|
|
12
|
+
identify?: (context: {
|
|
13
|
+
cookies: Record<string, string>;
|
|
14
|
+
headers: Headers;
|
|
15
|
+
url: URL;
|
|
16
|
+
}) => FlagifyUser | undefined;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Shape of a flag definition passed to defineFlag().
|
|
20
|
+
*/
|
|
21
|
+
interface FlagDefinition<T> {
|
|
22
|
+
key: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
default: T;
|
|
25
|
+
options?: Array<T | {
|
|
26
|
+
value: T;
|
|
27
|
+
label: string;
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* What gets stored in context.locals by the middleware.
|
|
32
|
+
*/
|
|
33
|
+
interface FlagifyLocals {
|
|
34
|
+
flagifyOverrides: Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Astro integration for Flagify feature flags.
|
|
39
|
+
*
|
|
40
|
+
* Registers middleware for flag evaluation and a dev toolbar app
|
|
41
|
+
* for toggling flag overrides during development.
|
|
42
|
+
*
|
|
43
|
+
* ```js
|
|
44
|
+
* // astro.config.mjs
|
|
45
|
+
* import { defineConfig } from 'astro/config';
|
|
46
|
+
* import flagify from '@flagify/astro';
|
|
47
|
+
*
|
|
48
|
+
* export default defineConfig({
|
|
49
|
+
* integrations: [flagify()],
|
|
50
|
+
* });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
declare function flagifyIntegration(_options?: FlagifyAstroOptions): {
|
|
54
|
+
name: string;
|
|
55
|
+
hooks: Record<string, (params: any) => void>;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
interface FlagEvaluator<T> {
|
|
59
|
+
(astro: {
|
|
60
|
+
cookies: {
|
|
61
|
+
get(name: string): {
|
|
62
|
+
value: string;
|
|
63
|
+
} | undefined;
|
|
64
|
+
};
|
|
65
|
+
}): Promise<T>;
|
|
66
|
+
_definition: FlagDefinition<T>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Define a feature flag that can be evaluated in Astro pages and components.
|
|
70
|
+
*
|
|
71
|
+
* Returns an async function that accepts the Astro global object
|
|
72
|
+
* and resolves the flag value with this priority:
|
|
73
|
+
* 1. Override cookie value (for dev testing)
|
|
74
|
+
* 2. Flagify SDK evaluation
|
|
75
|
+
* 3. Default fallback
|
|
76
|
+
*/
|
|
77
|
+
declare function defineFlag<T>(definition: FlagDefinition<T>): FlagEvaluator<T>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Initialize the singleton Flagify client.
|
|
81
|
+
* Subsequent calls are no-ops if the client already exists.
|
|
82
|
+
*/
|
|
83
|
+
declare function initClient(config: FlagifyOptions): void;
|
|
84
|
+
/**
|
|
85
|
+
* Returns the singleton Flagify client, or null if not initialized.
|
|
86
|
+
*/
|
|
87
|
+
declare function getClient(): Flagify | null;
|
|
88
|
+
/**
|
|
89
|
+
* Waits for the client to complete its initial flag sync.
|
|
90
|
+
*/
|
|
91
|
+
declare function waitForClient(): Promise<Flagify | null>;
|
|
92
|
+
/**
|
|
93
|
+
* Destroy the singleton client and free resources.
|
|
94
|
+
*/
|
|
95
|
+
declare function destroyClient(): void;
|
|
96
|
+
|
|
97
|
+
export { type FlagDefinition, type FlagEvaluator, type FlagifyAstroOptions, type FlagifyLocals, flagifyIntegration as default, defineFlag, destroyClient, getClient, initClient, waitForClient };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { FlagifyOptions, FlagifyUser, Flagify } from '@flagify/node';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Options passed to the flagify() integration in astro.config.mjs.
|
|
5
|
+
*/
|
|
6
|
+
interface FlagifyAstroOptions extends FlagifyOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Function to extract user context from the request.
|
|
9
|
+
* Called by middleware on each request.
|
|
10
|
+
* If not provided, no user context is set.
|
|
11
|
+
*/
|
|
12
|
+
identify?: (context: {
|
|
13
|
+
cookies: Record<string, string>;
|
|
14
|
+
headers: Headers;
|
|
15
|
+
url: URL;
|
|
16
|
+
}) => FlagifyUser | undefined;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Shape of a flag definition passed to defineFlag().
|
|
20
|
+
*/
|
|
21
|
+
interface FlagDefinition<T> {
|
|
22
|
+
key: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
default: T;
|
|
25
|
+
options?: Array<T | {
|
|
26
|
+
value: T;
|
|
27
|
+
label: string;
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* What gets stored in context.locals by the middleware.
|
|
32
|
+
*/
|
|
33
|
+
interface FlagifyLocals {
|
|
34
|
+
flagifyOverrides: Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Astro integration for Flagify feature flags.
|
|
39
|
+
*
|
|
40
|
+
* Registers middleware for flag evaluation and a dev toolbar app
|
|
41
|
+
* for toggling flag overrides during development.
|
|
42
|
+
*
|
|
43
|
+
* ```js
|
|
44
|
+
* // astro.config.mjs
|
|
45
|
+
* import { defineConfig } from 'astro/config';
|
|
46
|
+
* import flagify from '@flagify/astro';
|
|
47
|
+
*
|
|
48
|
+
* export default defineConfig({
|
|
49
|
+
* integrations: [flagify()],
|
|
50
|
+
* });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
declare function flagifyIntegration(_options?: FlagifyAstroOptions): {
|
|
54
|
+
name: string;
|
|
55
|
+
hooks: Record<string, (params: any) => void>;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
interface FlagEvaluator<T> {
|
|
59
|
+
(astro: {
|
|
60
|
+
cookies: {
|
|
61
|
+
get(name: string): {
|
|
62
|
+
value: string;
|
|
63
|
+
} | undefined;
|
|
64
|
+
};
|
|
65
|
+
}): Promise<T>;
|
|
66
|
+
_definition: FlagDefinition<T>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Define a feature flag that can be evaluated in Astro pages and components.
|
|
70
|
+
*
|
|
71
|
+
* Returns an async function that accepts the Astro global object
|
|
72
|
+
* and resolves the flag value with this priority:
|
|
73
|
+
* 1. Override cookie value (for dev testing)
|
|
74
|
+
* 2. Flagify SDK evaluation
|
|
75
|
+
* 3. Default fallback
|
|
76
|
+
*/
|
|
77
|
+
declare function defineFlag<T>(definition: FlagDefinition<T>): FlagEvaluator<T>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Initialize the singleton Flagify client.
|
|
81
|
+
* Subsequent calls are no-ops if the client already exists.
|
|
82
|
+
*/
|
|
83
|
+
declare function initClient(config: FlagifyOptions): void;
|
|
84
|
+
/**
|
|
85
|
+
* Returns the singleton Flagify client, or null if not initialized.
|
|
86
|
+
*/
|
|
87
|
+
declare function getClient(): Flagify | null;
|
|
88
|
+
/**
|
|
89
|
+
* Waits for the client to complete its initial flag sync.
|
|
90
|
+
*/
|
|
91
|
+
declare function waitForClient(): Promise<Flagify | null>;
|
|
92
|
+
/**
|
|
93
|
+
* Destroy the singleton client and free resources.
|
|
94
|
+
*/
|
|
95
|
+
declare function destroyClient(): void;
|
|
96
|
+
|
|
97
|
+
export { type FlagDefinition, type FlagEvaluator, type FlagifyAstroOptions, type FlagifyLocals, flagifyIntegration as default, defineFlag, destroyClient, getClient, initClient, waitForClient };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {
|
|
2
|
+
destroyClient,
|
|
3
|
+
getClient,
|
|
4
|
+
initClient,
|
|
5
|
+
waitForClient
|
|
6
|
+
} from "./chunk-XPJW7PJL.js";
|
|
7
|
+
|
|
8
|
+
// src/integration.ts
|
|
9
|
+
var FLAG_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/><line x1="4" y1="22" x2="4" y2="15"/></svg>`;
|
|
10
|
+
function flagifyIntegration(_options) {
|
|
11
|
+
return {
|
|
12
|
+
name: "@flagify/astro",
|
|
13
|
+
hooks: {
|
|
14
|
+
"astro:config:setup": ({
|
|
15
|
+
addMiddleware,
|
|
16
|
+
addDevToolbarApp
|
|
17
|
+
}) => {
|
|
18
|
+
addMiddleware({
|
|
19
|
+
entrypoint: "@flagify/astro/middleware",
|
|
20
|
+
order: "pre"
|
|
21
|
+
});
|
|
22
|
+
addDevToolbarApp({
|
|
23
|
+
id: "flagify-flags",
|
|
24
|
+
name: "Feature Flags",
|
|
25
|
+
icon: FLAG_ICON,
|
|
26
|
+
entrypoint: new URL("./toolbar/app.js", import.meta.url)
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// src/flag.ts
|
|
34
|
+
var OVERRIDE_COOKIE = "flagify-overrides";
|
|
35
|
+
function defineFlag(definition) {
|
|
36
|
+
const evaluate = async (astro) => {
|
|
37
|
+
const overrideCookie = astro.cookies.get(OVERRIDE_COOKIE);
|
|
38
|
+
if (overrideCookie) {
|
|
39
|
+
try {
|
|
40
|
+
const overrides = JSON.parse(overrideCookie.value);
|
|
41
|
+
if (definition.key in overrides) {
|
|
42
|
+
return overrides[definition.key];
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const client = getClient();
|
|
48
|
+
if (!client) return definition.default;
|
|
49
|
+
if (typeof definition.default === "boolean") {
|
|
50
|
+
return client.isEnabled(definition.key);
|
|
51
|
+
}
|
|
52
|
+
return client.getValue(definition.key, definition.default);
|
|
53
|
+
};
|
|
54
|
+
evaluate._definition = definition;
|
|
55
|
+
return evaluate;
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
flagifyIntegration as default,
|
|
59
|
+
defineFlag,
|
|
60
|
+
destroyClient,
|
|
61
|
+
getClient,
|
|
62
|
+
initClient,
|
|
63
|
+
waitForClient
|
|
64
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/middleware.ts
|
|
21
|
+
var middleware_exports = {};
|
|
22
|
+
__export(middleware_exports, {
|
|
23
|
+
onRequest: () => onRequest
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(middleware_exports);
|
|
26
|
+
|
|
27
|
+
// src/client.ts
|
|
28
|
+
var import_node = require("@flagify/node");
|
|
29
|
+
var client = null;
|
|
30
|
+
var clientReady = null;
|
|
31
|
+
function initClient(config) {
|
|
32
|
+
if (client) return;
|
|
33
|
+
client = new import_node.Flagify(config);
|
|
34
|
+
clientReady = client.ready();
|
|
35
|
+
}
|
|
36
|
+
async function waitForClient() {
|
|
37
|
+
if (clientReady) await clientReady;
|
|
38
|
+
return client;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/middleware.ts
|
|
42
|
+
var import_meta = {};
|
|
43
|
+
var OVERRIDE_COOKIE = "flagify-overrides";
|
|
44
|
+
var onRequest = async (context, next) => {
|
|
45
|
+
const env = import_meta.env ?? {};
|
|
46
|
+
const projectKey = env.FLAGIFY_PROJECT_KEY ?? "";
|
|
47
|
+
const publicKey = env.FLAGIFY_PUBLIC_KEY ?? "";
|
|
48
|
+
const secretKey = env.FLAGIFY_SECRET_KEY;
|
|
49
|
+
if (projectKey && publicKey) {
|
|
50
|
+
initClient({ projectKey, publicKey, secretKey });
|
|
51
|
+
await waitForClient();
|
|
52
|
+
}
|
|
53
|
+
let overrides = {};
|
|
54
|
+
const overrideCookie = context.cookies?.get(OVERRIDE_COOKIE);
|
|
55
|
+
if (overrideCookie) {
|
|
56
|
+
try {
|
|
57
|
+
overrides = JSON.parse(overrideCookie.value);
|
|
58
|
+
} catch {
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
context.locals.flagifyOverrides = overrides;
|
|
62
|
+
return next();
|
|
63
|
+
};
|
|
64
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
65
|
+
0 && (module.exports = {
|
|
66
|
+
onRequest
|
|
67
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Astro middleware that initializes the Flagify client and parses
|
|
3
|
+
* override cookies on each request.
|
|
4
|
+
*
|
|
5
|
+
* Usage in astro.config.mjs — this is auto-injected by the integration,
|
|
6
|
+
* but can also be used standalone:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* // src/middleware.ts
|
|
10
|
+
* export { onRequest } from '@flagify/astro/middleware';
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
declare const onRequest: (context: any, next: () => Promise<Response>) => Promise<Response>;
|
|
14
|
+
|
|
15
|
+
export { onRequest };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Astro middleware that initializes the Flagify client and parses
|
|
3
|
+
* override cookies on each request.
|
|
4
|
+
*
|
|
5
|
+
* Usage in astro.config.mjs — this is auto-injected by the integration,
|
|
6
|
+
* but can also be used standalone:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* // src/middleware.ts
|
|
10
|
+
* export { onRequest } from '@flagify/astro/middleware';
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
declare const onRequest: (context: any, next: () => Promise<Response>) => Promise<Response>;
|
|
14
|
+
|
|
15
|
+
export { onRequest };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
initClient,
|
|
3
|
+
waitForClient
|
|
4
|
+
} from "./chunk-XPJW7PJL.js";
|
|
5
|
+
|
|
6
|
+
// src/middleware.ts
|
|
7
|
+
var OVERRIDE_COOKIE = "flagify-overrides";
|
|
8
|
+
var onRequest = async (context, next) => {
|
|
9
|
+
const env = import.meta.env ?? {};
|
|
10
|
+
const projectKey = env.FLAGIFY_PROJECT_KEY ?? "";
|
|
11
|
+
const publicKey = env.FLAGIFY_PUBLIC_KEY ?? "";
|
|
12
|
+
const secretKey = env.FLAGIFY_SECRET_KEY;
|
|
13
|
+
if (projectKey && publicKey) {
|
|
14
|
+
initClient({ projectKey, publicKey, secretKey });
|
|
15
|
+
await waitForClient();
|
|
16
|
+
}
|
|
17
|
+
let overrides = {};
|
|
18
|
+
const overrideCookie = context.cookies?.get(OVERRIDE_COOKIE);
|
|
19
|
+
if (overrideCookie) {
|
|
20
|
+
try {
|
|
21
|
+
overrides = JSON.parse(overrideCookie.value);
|
|
22
|
+
} catch {
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
context.locals.flagifyOverrides = overrides;
|
|
26
|
+
return next();
|
|
27
|
+
};
|
|
28
|
+
export {
|
|
29
|
+
onRequest
|
|
30
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/toolbar/app.ts
|
|
21
|
+
var app_exports = {};
|
|
22
|
+
__export(app_exports, {
|
|
23
|
+
default: () => app_default
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(app_exports);
|
|
26
|
+
var OVERRIDE_COOKIE = "flagify-overrides";
|
|
27
|
+
function getCookieOverrides() {
|
|
28
|
+
const match = document.cookie.match(
|
|
29
|
+
new RegExp(`${OVERRIDE_COOKIE}=([^;]+)`)
|
|
30
|
+
);
|
|
31
|
+
if (!match) return {};
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(decodeURIComponent(match[1]));
|
|
34
|
+
} catch {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function setCookieOverrides(overrides) {
|
|
39
|
+
document.cookie = `${OVERRIDE_COOKIE}=${encodeURIComponent(JSON.stringify(overrides))}; path=/; max-age=86400`;
|
|
40
|
+
}
|
|
41
|
+
var app_default = {
|
|
42
|
+
id: "flagify-flags",
|
|
43
|
+
name: "Feature Flags",
|
|
44
|
+
init(canvas) {
|
|
45
|
+
const overrides = getCookieOverrides();
|
|
46
|
+
const container = document.createElement("astro-dev-toolbar-window");
|
|
47
|
+
container.innerHTML = `
|
|
48
|
+
<div style="padding: 12px; font-family: system-ui, sans-serif;">
|
|
49
|
+
<h2 style="margin: 0 0 8px; font-size: 15px; font-weight: 600;">Flagify Overrides</h2>
|
|
50
|
+
<p style="font-size: 13px; opacity: 0.7; margin: 0 0 12px;">
|
|
51
|
+
Add flag overrides for local development.
|
|
52
|
+
</p>
|
|
53
|
+
<textarea
|
|
54
|
+
id="flagify-overrides-editor"
|
|
55
|
+
style="width: 100%; min-height: 140px; font-family: monospace; font-size: 13px; padding: 8px; border-radius: 6px; border: 1px solid rgba(255,255,255,0.15); background: rgba(0,0,0,0.3); color: inherit; resize: vertical;"
|
|
56
|
+
>${JSON.stringify(overrides, null, 2)}</textarea>
|
|
57
|
+
<div style="margin-top: 8px; display: flex; gap: 8px;">
|
|
58
|
+
<button id="flagify-save" style="padding: 6px 14px; border-radius: 6px; border: none; background: #6366f1; color: white; cursor: pointer; font-size: 13px;">
|
|
59
|
+
Save & Reload
|
|
60
|
+
</button>
|
|
61
|
+
<button id="flagify-clear" style="padding: 6px 14px; border-radius: 6px; border: 1px solid rgba(255,255,255,0.2); background: transparent; color: inherit; cursor: pointer; font-size: 13px;">
|
|
62
|
+
Clear All
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
`;
|
|
67
|
+
canvas.appendChild(container);
|
|
68
|
+
container.querySelector("#flagify-save")?.addEventListener("click", () => {
|
|
69
|
+
const textarea = container.querySelector(
|
|
70
|
+
"#flagify-overrides-editor"
|
|
71
|
+
);
|
|
72
|
+
try {
|
|
73
|
+
const parsed = JSON.parse(textarea.value);
|
|
74
|
+
setCookieOverrides(parsed);
|
|
75
|
+
window.location.reload();
|
|
76
|
+
} catch {
|
|
77
|
+
alert("Invalid JSON");
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
container.querySelector("#flagify-clear")?.addEventListener("click", () => {
|
|
81
|
+
document.cookie = `${OVERRIDE_COOKIE}=; path=/; max-age=0`;
|
|
82
|
+
window.location.reload();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// src/toolbar/app.ts
|
|
2
|
+
var OVERRIDE_COOKIE = "flagify-overrides";
|
|
3
|
+
function getCookieOverrides() {
|
|
4
|
+
const match = document.cookie.match(
|
|
5
|
+
new RegExp(`${OVERRIDE_COOKIE}=([^;]+)`)
|
|
6
|
+
);
|
|
7
|
+
if (!match) return {};
|
|
8
|
+
try {
|
|
9
|
+
return JSON.parse(decodeURIComponent(match[1]));
|
|
10
|
+
} catch {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function setCookieOverrides(overrides) {
|
|
15
|
+
document.cookie = `${OVERRIDE_COOKIE}=${encodeURIComponent(JSON.stringify(overrides))}; path=/; max-age=86400`;
|
|
16
|
+
}
|
|
17
|
+
var app_default = {
|
|
18
|
+
id: "flagify-flags",
|
|
19
|
+
name: "Feature Flags",
|
|
20
|
+
init(canvas) {
|
|
21
|
+
const overrides = getCookieOverrides();
|
|
22
|
+
const container = document.createElement("astro-dev-toolbar-window");
|
|
23
|
+
container.innerHTML = `
|
|
24
|
+
<div style="padding: 12px; font-family: system-ui, sans-serif;">
|
|
25
|
+
<h2 style="margin: 0 0 8px; font-size: 15px; font-weight: 600;">Flagify Overrides</h2>
|
|
26
|
+
<p style="font-size: 13px; opacity: 0.7; margin: 0 0 12px;">
|
|
27
|
+
Add flag overrides for local development.
|
|
28
|
+
</p>
|
|
29
|
+
<textarea
|
|
30
|
+
id="flagify-overrides-editor"
|
|
31
|
+
style="width: 100%; min-height: 140px; font-family: monospace; font-size: 13px; padding: 8px; border-radius: 6px; border: 1px solid rgba(255,255,255,0.15); background: rgba(0,0,0,0.3); color: inherit; resize: vertical;"
|
|
32
|
+
>${JSON.stringify(overrides, null, 2)}</textarea>
|
|
33
|
+
<div style="margin-top: 8px; display: flex; gap: 8px;">
|
|
34
|
+
<button id="flagify-save" style="padding: 6px 14px; border-radius: 6px; border: none; background: #6366f1; color: white; cursor: pointer; font-size: 13px;">
|
|
35
|
+
Save & Reload
|
|
36
|
+
</button>
|
|
37
|
+
<button id="flagify-clear" style="padding: 6px 14px; border-radius: 6px; border: 1px solid rgba(255,255,255,0.2); background: transparent; color: inherit; cursor: pointer; font-size: 13px;">
|
|
38
|
+
Clear All
|
|
39
|
+
</button>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
`;
|
|
43
|
+
canvas.appendChild(container);
|
|
44
|
+
container.querySelector("#flagify-save")?.addEventListener("click", () => {
|
|
45
|
+
const textarea = container.querySelector(
|
|
46
|
+
"#flagify-overrides-editor"
|
|
47
|
+
);
|
|
48
|
+
try {
|
|
49
|
+
const parsed = JSON.parse(textarea.value);
|
|
50
|
+
setCookieOverrides(parsed);
|
|
51
|
+
window.location.reload();
|
|
52
|
+
} catch {
|
|
53
|
+
alert("Invalid JSON");
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
container.querySelector("#flagify-clear")?.addEventListener("click", () => {
|
|
57
|
+
document.cookie = `${OVERRIDE_COOKIE}=; path=/; max-age=0`;
|
|
58
|
+
window.location.reload();
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
export {
|
|
63
|
+
app_default as default
|
|
64
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flagify/astro",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Astro integration for Flagify — feature flags with dev toolbar and middleware.",
|
|
5
|
+
"author": "Mario Campbell R <mario@mariocampbellr.com>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/index.cjs",
|
|
9
|
+
"module": "dist/index.js",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.cjs"
|
|
16
|
+
},
|
|
17
|
+
"./middleware": {
|
|
18
|
+
"types": "./dist/middleware.d.ts",
|
|
19
|
+
"import": "./dist/middleware.js",
|
|
20
|
+
"require": "./dist/middleware.cjs"
|
|
21
|
+
},
|
|
22
|
+
"./adapter": {
|
|
23
|
+
"types": "./dist/adapter.d.ts",
|
|
24
|
+
"import": "./dist/adapter.js",
|
|
25
|
+
"require": "./dist/adapter.cjs"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist"
|
|
30
|
+
],
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@flagify/node": "1.0.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"astro": ">=4.0.0"
|
|
36
|
+
},
|
|
37
|
+
"peerDependenciesMeta": {
|
|
38
|
+
"flags": {
|
|
39
|
+
"optional": true
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"astro": "^4.0.0"
|
|
44
|
+
},
|
|
45
|
+
"keywords": [
|
|
46
|
+
"astro-integration",
|
|
47
|
+
"feature-flags",
|
|
48
|
+
"flagify",
|
|
49
|
+
"feature-toggle"
|
|
50
|
+
],
|
|
51
|
+
"homepage": "https://flagify.dev",
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "https://github.com/flagifyhq/javascript",
|
|
55
|
+
"directory": "packages/astro"
|
|
56
|
+
},
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/flagifyhq/javascript/issues"
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"build": "tsup src/index.ts src/middleware.ts src/adapter.ts src/toolbar/app.ts --format esm,cjs --dts",
|
|
65
|
+
"dev": "tsup src/index.ts src/middleware.ts src/adapter.ts src/toolbar/app.ts --format esm,cjs --dts --watch",
|
|
66
|
+
"test": "vitest run",
|
|
67
|
+
"lint": "tsc --noEmit",
|
|
68
|
+
"clean": "rm -rf dist"
|
|
69
|
+
}
|
|
70
|
+
}
|