@guidelytech/flags 0.0.0-stage → 0.8.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Guidely
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 CHANGED
@@ -1,3 +1,182 @@
1
- # Temporary Holding Version
1
+ # @guidelytech/flags
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Guidely feature flags, remote config and experiments for TypeScript. One client for browsers,
4
+ Node.js ≥ 18, Next.js (server and edge), Deno, Bun and React Native; no runtime dependencies.
5
+
6
+ If you already use a Guidely SDK (`@guidelytech/react`, `nextjs`, `react-native`, `svelte`,
7
+ `sveltekit`, `sdk-core` or the script tag), flags are built in: `guidely.flags`, `useFlag()`,
8
+ `flag()`… with identity, page and device attributes filled in for you. Use this package directly
9
+ for servers, edge functions and flags-only apps.
10
+
11
+ ```bash
12
+ pnpm add @guidelytech/flags
13
+ ```
14
+
15
+ ## Keys
16
+
17
+ * **Public key** `pk_…` — the environment key you already use as `projectId` for tracking. Works
18
+ everywhere, with no other configuration.
19
+ * **Secret key** `gly_sk_…` (scope `flags:read`, servers only) — also needs `environment`. Gets
20
+ server-only flags, and works when the environment hides its rules from public keys.
21
+
22
+ ## Browser
23
+
24
+ ```ts
25
+ import { createFlagsClient } from "@guidelytech/flags";
26
+
27
+ const flags = createFlagsClient({
28
+ sdkKey: "pk_prod_…",
29
+ apiUrl: "https://guidelytech.com",
30
+ attributes: { id: user.id, plan: user.plan, account: { id: org.id } },
31
+ });
32
+
33
+ await flags.ready();
34
+ if (flags.isEnabled("new-checkout")) showNewCheckout();
35
+ const color = flags.getString("checkout-button-color", "blue");
36
+ flags.subscribe(() => render()); // payload, attribute or override changes
37
+ flags.updateAttributes({ plan: "enterprise" });
38
+ ```
39
+
40
+ The last payload is kept in `localStorage`, so returning visitors get flags instantly and offline.
41
+
42
+ ## Node.js / server
43
+
44
+ One client per process; evaluate per request with `forUser()` (it never changes the client):
45
+
46
+ ```ts
47
+ import { createFlagsClient } from "@guidelytech/flags";
48
+
49
+ export const flags = createFlagsClient({ sdkKey: process.env.GUIDELY_SDK_KEY!, environment: "production" });
50
+
51
+ app.get("/checkout", async (req, res) => {
52
+ await flags.ready(); // resolves once, after the first download (or failure)
53
+ const f = flags.forUser({ id: req.user.id, plan: req.user.plan });
54
+ res.render("checkout", { newCheckout: f.isEnabled("new-checkout"), limits: f.getJson("limits", {}) });
55
+ });
56
+ ```
57
+
58
+ ## Next.js
59
+
60
+ Use `@guidelytech/nextjs/server` (`getFlags()`, `getFlagsBootstrap()`): it keeps one client across
61
+ requests and hot reloads and bypasses the Next.js fetch cache. Pass `getFlagsBootstrap()` to the
62
+ browser provider to render the same values on the server and the client.
63
+
64
+ ## Edge runtimes and serverless
65
+
66
+ Instances are short-lived, so don't poll — refresh on read once the cache is stale:
67
+
68
+ ```ts
69
+ const flags = createFlagsClient({ sdkKey: "pk_prod_…", pollingMode: "lazy", cacheTtlSeconds: 30 });
70
+ ```
71
+
72
+ ## API
73
+
74
+ | | |
75
+ |---|---|
76
+ | `ready()` / `isReady()` | First load settled (bootstrap, network success or failure). Never rejects. |
77
+ | `isEnabled(key, fallback = false)` | Boolean flags only: any other value gives `fallback`. |
78
+ | `getValue(key, fallback)` | Value, or `fallback` when missing, null or of another JSON type. |
79
+ | `getString` / `getNumber` / `getBoolean` / `getJson` | Typed getters with the same rule. |
80
+ | `getDetails(key, fallback)` | Value plus `reason`, `ruleId`, `variationId`, `experiment`, `usedFallback`. |
81
+ | `getAll()` | Every flag's value (records no exposures). |
82
+ | `forUser(attributes)` | Getters for other attributes (merged over the client's). |
83
+ | `setAttributes` / `updateAttributes` / `getAttributes` | Replace / merge / read the attributes. |
84
+ | `setOverrides(map \| null)` | Local values (see `overrideBehaviour`). |
85
+ | `setOffline(bool)` / `refresh()` | Pause network activity / fetch now. |
86
+ | `getPayload()` | The payload (or remote results) to bootstrap a browser from server rendering. |
87
+ | `subscribe(listener)` | Change notifications; returns `unsubscribe` (works with `useSyncExternalStore`). |
88
+ | `flush()` / `destroy()` | Send usage now / stop timers and streams. |
89
+
90
+ Getters never throw and never block: before flags load (or if Guidely is unreachable) they return
91
+ your fallback, and afterwards the last good payload.
92
+
93
+ ### Options
94
+
95
+ | Option | Default | |
96
+ |---|---|---|
97
+ | `sdkKey` (alias `projectId`) | — | `pk_…` or `gly_sk_…`. |
98
+ | `apiUrl` | `https://guidelytech.com` | Your Guidely URL. |
99
+ | `environment` | — | Required with `gly_sk_` keys. |
100
+ | `attributes` | `{}` | Default attributes. |
101
+ | `pollingMode` | `auto` | `auto` (every `pollIntervalSeconds`, default 60), `lazy` (on read after `cacheTtlSeconds`), `manual` (`refresh()` only), `stream` (server-sent events, polling while disconnected). |
102
+ | `payload` / `remoteResults` | — | Bootstrap: used immediately (`ready` at once). |
103
+ | `remoteEval` | `false` | Evaluate on the server (`POST /flags/evaluate`) for environments that hide their rules. Switched on automatically when a public key gets `remote_evaluation_required`. Re-evaluates (debounced) when attributes change; results are cached per attribute set. |
104
+ | `decryptionKey` | — | For encrypted payloads (AES-256-GCM via WebCrypto). |
105
+ | `overrides` / `overrideBehaviour` | — / `localOverRemote` | `localOnly` (never fetch; overrides, then the bootstrap `payload`), `localOverRemote` (overrides win), `remoteOverLocal` (only for flags missing remotely). |
106
+ | `devOverrides` | `true` | Read [dev overrides](#dev-overrides-ci-and-tests) from environment variables and test globals. |
107
+ | `enableUrlOverrides` | `false` | Browser QA: `?guidely_flags=new-checkout:true,checkout-button-color:"green"`. |
108
+ | `stickyBucketService` | — | `InMemoryStickyBucketStore` / `LocalStorageStickyBucketStore`: users keep their experiment variation. |
109
+ | `storage` | `localStorage` in browsers | Any `getItem`/`setItem` store, sync or async (AsyncStorage). `false` disables. |
110
+ | `trackingCallback(experiment, result)` | — | Once per (experiment, user, variation): send exposures to your analytics. |
111
+ | `trackExposures` | `true` unless `trackingCallback` | Send exposures to Guidely for experiment results. |
112
+ | `reportUsage` | `true` | Aggregated evaluation counts, at most every 60 s (`sendBeacon` on page hide). |
113
+ | `onReady`, `onChange`, `onFlagEvaluated`, `onError` | — | Hooks. |
114
+ | `offline`, `fetch`, `logger`, `now` | — | Start offline; custom fetch; `null` silences logs; clock for tests. |
115
+
116
+ `createStaticFlags({ payload, attributes })` gives the same getters over a payload with no network
117
+ or tracking — framework bindings use it to server-render and hydrate.
118
+
119
+ ## Dev overrides (CI and tests)
120
+
121
+ Pin flag values without touching the app. Every client reads these once, when it is created:
122
+
123
+ | Source | Example |
124
+ |---|---|
125
+ | `GUIDELY_FLAG_OVERRIDES` | `new-checkout:true,limit:5,theme:"dark"` or `{"new-checkout":true}` |
126
+ | `GUIDELY_FLAG_OVERRIDES_FILE` | path to a file holding the same text (JSON object or one `key:value` per line) |
127
+ | `GUIDELY_FLAG_OVERRIDE_BEHAVIOUR` | `localOnly` — no network, no cached payload: hermetic tests |
128
+ | `globalThis.__GUIDELY_FLAG_OVERRIDES__` / `__GUIDELY_FLAG_OVERRIDE_BEHAVIOUR__` | an object or the same text, set by a test runner in the page |
129
+
130
+ ```bash
131
+ GUIDELY_FLAG_OVERRIDES='new-checkout:true' GUIDELY_FLAG_OVERRIDE_BEHAVIOUR=localOnly pnpm test
132
+ GUIDELY_FLAG_OVERRIDES_FILE=ci/flags.json pnpm test
133
+ ```
134
+
135
+ A sample `ci/flags.json` (keys are flag keys; give each value the flag's type, since a value of the
136
+ wrong type or `null` makes the getter return its fallback):
137
+
138
+ ```json
139
+ {
140
+ "new-checkout": true,
141
+ "checkout-button-color": "green",
142
+ "max-cart-items": 25,
143
+ "pricing-page": { "layout": "grid", "plans": ["starter", "pro", "team"] },
144
+ "holiday-banner": null
145
+ }
146
+ ```
147
+
148
+ ```ts
149
+ // Playwright: set before the page's scripts run.
150
+ await page.addInitScript(() => {
151
+ (window as any).__GUIDELY_FLAG_OVERRIDES__ = { "new-checkout": true };
152
+ });
153
+ // Cypress: cy.visit("/", { onBeforeLoad: (win) => { win.__GUIDELY_FLAG_OVERRIDES__ = { "new-checkout": true }; } });
154
+ ```
155
+
156
+ Per flag, later wins: `overrides` / `setOverrides()` → file → variable → global → `?guidely_flags=`.
157
+ `setOverrides()` only replaces the first. Values are served with reason `OVERRIDE`. The client
158
+ logs one warning listing the sources in use, so a variable left set in production shows up in
159
+ logs; malformed sources go to `onError` and are ignored. `devOverrides: false` turns it all off.
160
+
161
+ With server rendering, set both: the variable for server code and the global for the browser
162
+ (React and Svelte server renders read the bootstrap payload, which carries no overrides).
163
+ `parseOverrides(text)` is exported for reading the same format elsewhere.
164
+
165
+ ## CLI
166
+
167
+ ```bash
168
+ # In CI on your main branch: upload where each flag is used in code (shown on the flag's page).
169
+ npx guidely-flags code-refs --key "$GUIDELY_API_KEY" --environment production --repo acme/web
170
+
171
+ # Debug an evaluation.
172
+ npx guidely-flags evaluate --key pk_prod_… --flag new-checkout --attributes '{"id":"u1","plan":"pro"}'
173
+ ```
174
+
175
+ `code-refs` needs a `gly_sk_` key with `flags:read` and `flags:code-refs`. It scans files from
176
+ `git ls-files` (or a walk skipping `node_modules`, `dist`, `build`, `vendor`…) under 1 MB for flag
177
+ keys written as quoted string literals, and uploads `{ repo, branch, commit, refs }`. Branch and
178
+ commit default to the current git checkout (or `GITHUB_REF_NAME` / `GITHUB_SHA`). `--dry-run`
179
+ prints the references instead. `guidely-flags --help` lists every option.
180
+
181
+ The evaluation semantics are specified in
182
+ [docs/feature-flags-spec.md](https://github.com/parmarg/Guidely/blob/main/docs/feature-flags-spec.md).