@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 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
@@ -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 };
@@ -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
+ });
@@ -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 };
@@ -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 &amp; 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,7 @@
1
+ declare const _default: {
2
+ id: string;
3
+ name: string;
4
+ init(canvas: any): void;
5
+ };
6
+
7
+ export { _default as default };
@@ -0,0 +1,7 @@
1
+ declare const _default: {
2
+ id: string;
3
+ name: string;
4
+ init(canvas: any): void;
5
+ };
6
+
7
+ export { _default as default };
@@ -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 &amp; 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
+ }