@guidelytech/sveltekit 0.6.2 → 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 +21 -0
- package/README.md +66 -1
- package/dist/index.cjs +70 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -1
- package/dist/index.d.ts +50 -1
- package/dist/index.js +68 -7
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +85 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +49 -0
- package/dist/server.d.ts +49 -0
- package/dist/server.js +58 -0
- package/dist/server.js.map +1 -0
- package/package.json +10 -4
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
|
@@ -50,4 +50,69 @@ next time they load the app.
|
|
|
50
50
|
| `initGuidely(options)` | Call once during root layout initialisation. Returns the client in the browser, `null` on the server. Uses `afterNavigate` from `$app/navigation` to call `page()` and destroys the client when the layout is destroyed. |
|
|
51
51
|
| `guidelyEnv(env)` | Reads `PUBLIC_GUIDELY_PROJECT_ID`/`_ENV`/`_API_URL`/`_RELEASE` (`$env/static/public`, `$env/dynamic/public`) or the `VITE_GUIDELY_*` equivalents (`import.meta.env`). |
|
|
52
52
|
| `syncGuidelyUser(user \| null \| undefined)` | Keeps the Guidely user in step with your session (re-exported from `@guidelytech/svelte`). |
|
|
53
|
-
| everything from `@guidelytech/svelte` | stores, actions and context helpers. |
|
|
53
|
+
| everything from `@guidelytech/svelte` | stores (including the flag stores `flag`, `featureValue`, `flagDetails`, `allFlags`, `flagsReady`), actions and context helpers. |
|
|
54
|
+
| `createServerFlags`, `getFlags`, `loadFlags` | Server helpers, also at `@guidelytech/sveltekit/server` (see below). |
|
|
55
|
+
|
|
56
|
+
## Feature flags
|
|
57
|
+
|
|
58
|
+
Flags are on with the same public key. Evaluate them on the server and hand the payload to the
|
|
59
|
+
browser so the first render matches — no flicker:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
// src/hooks.server.ts
|
|
63
|
+
import { createServerFlags } from "@guidelytech/sveltekit/server";
|
|
64
|
+
|
|
65
|
+
export const handle = async ({ event, resolve }) => {
|
|
66
|
+
// One client per process (polls Guidely, evaluates locally). Env: GUIDELY_SDK_KEY or PUBLIC_GUIDELY_PROJECT_ID.
|
|
67
|
+
event.locals.flags = createServerFlags().forUser({ id: event.locals.user?.id, plan: event.locals.user?.plan });
|
|
68
|
+
return resolve(event);
|
|
69
|
+
};
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
// src/routes/+layout.server.ts
|
|
74
|
+
import { loadFlags } from "@guidelytech/sveltekit/server";
|
|
75
|
+
export const load = async (event) => ({ user: event.locals.user ?? null, flags: await loadFlags(event) });
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```svelte
|
|
79
|
+
<!-- src/routes/+layout.svelte -->
|
|
80
|
+
<script lang="ts">
|
|
81
|
+
import { untrack } from "svelte";
|
|
82
|
+
let { data, children } = $props();
|
|
83
|
+
initGuidely({ ...guidelyEnv(env), flags: untrack(() => data.flags) }); // { payload, attributes }
|
|
84
|
+
</script>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```svelte
|
|
88
|
+
<!-- any component -->
|
|
89
|
+
<script lang="ts">
|
|
90
|
+
import { flag, featureValue } from "@guidelytech/sveltekit";
|
|
91
|
+
const newBanner = flag("new-dashboard-banner");
|
|
92
|
+
const color = featureValue("checkout-button-color", "blue");
|
|
93
|
+
</script>
|
|
94
|
+
{#if $newBanner}<NewBanner />{/if}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
In `+page.server.ts` and endpoints read `locals.flags.isEnabled("…")` / `getValue(…)` directly.
|
|
98
|
+
Add the type to `src/app.d.ts`:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
declare global {
|
|
102
|
+
namespace App {
|
|
103
|
+
interface Locals {
|
|
104
|
+
flags: import("@guidelytech/sveltekit/server").FlagsEvaluator;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export {};
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
| `@guidelytech/sveltekit/server` | Description |
|
|
112
|
+
|---|---|
|
|
113
|
+
| `createServerFlags(options?)` | The process-wide flags client (kept on `globalThis` across dev reloads). Options default to `GUIDELY_SDK_KEY` (`pk_…` or server-only `gly_sk_…`) or `PUBLIC_GUIDELY_PROJECT_ID`, `GUIDELY_API_URL` / `PUBLIC_GUIDELY_API_URL`, `GUIDELY_ENV` / `PUBLIC_GUIDELY_ENV`. On serverless adapters pass `{ pollingMode: "lazy" }`. |
|
|
114
|
+
| `getFlags(attributes, { client?, timeoutMs? })` | Getters for one user; waits up to `timeoutMs` (1500) for the first download. |
|
|
115
|
+
| `loadFlags(event \| attributes, { client?, timeoutMs? })` | `{ payload, attributes }` for `initGuidely({ flags })`; attributes come from `event.locals.flags`. The payload is `null` for a `gly_sk_` client (it contains server-only flags). |
|
|
116
|
+
|
|
117
|
+
Use the same attributes on the server as the browser will have (`id`, traits, `account`) so both
|
|
118
|
+
agree; rollouts and experiments for anonymous visitors (no id on the server) settle in the browser.
|
package/dist/index.cjs
CHANGED
|
@@ -21,8 +21,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
// src/index.ts
|
|
22
22
|
var index_exports = {};
|
|
23
23
|
__export(index_exports, {
|
|
24
|
+
createServerFlags: () => createServerFlags,
|
|
25
|
+
getFlags: () => getFlags,
|
|
24
26
|
guidelyEnv: () => guidelyEnv,
|
|
25
|
-
initGuidely: () => initGuidely
|
|
27
|
+
initGuidely: () => initGuidely,
|
|
28
|
+
loadFlags: () => loadFlags
|
|
26
29
|
});
|
|
27
30
|
module.exports = __toCommonJS(index_exports);
|
|
28
31
|
var import_environment = require("$app/environment");
|
|
@@ -30,13 +33,71 @@ var import_navigation = require("$app/navigation");
|
|
|
30
33
|
var import_svelte = require("@guidelytech/svelte");
|
|
31
34
|
var import_svelte2 = require("svelte");
|
|
32
35
|
__reExport(index_exports, require("@guidelytech/svelte"), module.exports);
|
|
36
|
+
|
|
37
|
+
// src/server.ts
|
|
38
|
+
var import_flags = require("@guidelytech/flags");
|
|
39
|
+
function registry() {
|
|
40
|
+
const g = globalThis;
|
|
41
|
+
return g.__guidelySvelteKitFlags ?? (g.__guidelySvelteKitFlags = { clients: /* @__PURE__ */ new Map(), current: null, warned: /* @__PURE__ */ new Set() });
|
|
42
|
+
}
|
|
43
|
+
function env(name) {
|
|
44
|
+
try {
|
|
45
|
+
return globalThis.process?.env?.[name] || void 0;
|
|
46
|
+
} catch {
|
|
47
|
+
return void 0;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function createServerFlags(options = {}) {
|
|
51
|
+
const sdkKey = options.sdkKey ?? options.projectId ?? env("GUIDELY_SDK_KEY") ?? env("PUBLIC_GUIDELY_PROJECT_ID");
|
|
52
|
+
if (!sdkKey) throw new Error("[Guidely] createServerFlags: pass `sdkKey` or set GUIDELY_SDK_KEY / PUBLIC_GUIDELY_PROJECT_ID.");
|
|
53
|
+
const apiUrl = options.apiUrl ?? env("GUIDELY_API_URL") ?? env("PUBLIC_GUIDELY_API_URL");
|
|
54
|
+
const environment = options.environment ?? env("GUIDELY_ENV") ?? env("PUBLIC_GUIDELY_ENV");
|
|
55
|
+
const id = `${sdkKey}|${apiUrl ?? ""}|${environment ?? ""}|${options.pollingMode ?? "auto"}`;
|
|
56
|
+
const reg = registry();
|
|
57
|
+
let client = reg.clients.get(id);
|
|
58
|
+
if (!client) {
|
|
59
|
+
client = (0, import_flags.createFlagsClient)({ storage: false, ...options, sdkKey, apiUrl, environment });
|
|
60
|
+
reg.clients.set(id, client);
|
|
61
|
+
}
|
|
62
|
+
reg.current = client;
|
|
63
|
+
return client;
|
|
64
|
+
}
|
|
65
|
+
function settle(client, timeoutMs) {
|
|
66
|
+
if (client.isReady()) return Promise.resolve();
|
|
67
|
+
return Promise.race([client.ready(), new Promise((r) => setTimeout(r, timeoutMs))]);
|
|
68
|
+
}
|
|
69
|
+
async function getFlags(attributes = {}, options = {}) {
|
|
70
|
+
const client = options.client ?? registry().current ?? createServerFlags();
|
|
71
|
+
await settle(client, options.timeoutMs ?? 1500);
|
|
72
|
+
return client.forUser(attributes);
|
|
73
|
+
}
|
|
74
|
+
async function loadFlags(input = {}, options = {}) {
|
|
75
|
+
const locals = input.locals;
|
|
76
|
+
const attributes = locals && typeof locals === "object" ? locals.flags?.getAttributes?.() ?? {} : input;
|
|
77
|
+
const client = options.client ?? registry().current ?? createServerFlags();
|
|
78
|
+
if (client.sdkKey.startsWith("gly_sk_")) {
|
|
79
|
+
const reg = registry();
|
|
80
|
+
if (!reg.warned.has(client.sdkKey)) {
|
|
81
|
+
reg.warned.add(client.sdkKey);
|
|
82
|
+
console.warn("[Guidely] loadFlags() needs a public-key client (pk_\u2026): a gly_sk_ payload includes server-only flags. Pass { client: createServerFlags({ sdkKey: PUBLIC_GUIDELY_PROJECT_ID }) }.");
|
|
83
|
+
}
|
|
84
|
+
return { payload: null, attributes };
|
|
85
|
+
}
|
|
86
|
+
await settle(client, options.timeoutMs ?? 1500);
|
|
87
|
+
return { payload: client.getPayload(), attributes };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/index.ts
|
|
33
91
|
function initGuidely(options) {
|
|
34
92
|
if (!import_environment.browser) {
|
|
35
93
|
(0, import_svelte.setGuidelyContext)(null);
|
|
94
|
+
const flags = options.flags || void 0;
|
|
95
|
+
(0, import_svelte.setFlagsContext)(flags?.payload ? (0, import_svelte.createStaticFlags)({ payload: flags.payload, attributes: flags.attributes }) : null);
|
|
36
96
|
return null;
|
|
37
97
|
}
|
|
38
98
|
const client = (0, import_svelte.createGuidely)(options);
|
|
39
99
|
(0, import_svelte.setGuidelyContext)(client);
|
|
100
|
+
(0, import_svelte.setFlagsContext)(client);
|
|
40
101
|
(0, import_navigation.afterNavigate)((nav) => {
|
|
41
102
|
if (nav.type !== "enter") client.page(window.location.href);
|
|
42
103
|
});
|
|
@@ -50,18 +111,21 @@ function guidelyStoreIs(client) {
|
|
|
50
111
|
import_svelte.guidely.subscribe((c) => current = c)();
|
|
51
112
|
return current === client;
|
|
52
113
|
}
|
|
53
|
-
function guidelyEnv(
|
|
114
|
+
function guidelyEnv(env2) {
|
|
54
115
|
return {
|
|
55
|
-
projectId:
|
|
56
|
-
environment:
|
|
57
|
-
apiUrl:
|
|
58
|
-
release:
|
|
116
|
+
projectId: env2.VITE_GUIDELY_PROJECT_ID || env2.PUBLIC_GUIDELY_PROJECT_ID || "",
|
|
117
|
+
environment: env2.VITE_GUIDELY_ENV || env2.PUBLIC_GUIDELY_ENV || void 0,
|
|
118
|
+
apiUrl: env2.VITE_GUIDELY_API_URL || env2.PUBLIC_GUIDELY_API_URL || void 0,
|
|
119
|
+
release: env2.VITE_GUIDELY_RELEASE || env2.PUBLIC_GUIDELY_RELEASE || void 0
|
|
59
120
|
};
|
|
60
121
|
}
|
|
61
122
|
// Annotate the CommonJS export names for ESM import in node:
|
|
62
123
|
0 && (module.exports = {
|
|
124
|
+
createServerFlags,
|
|
125
|
+
getFlags,
|
|
63
126
|
guidelyEnv,
|
|
64
127
|
initGuidely,
|
|
128
|
+
loadFlags,
|
|
65
129
|
...require("@guidelytech/svelte")
|
|
66
130
|
});
|
|
67
131
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { browser } from \"$app/environment\";\nimport { afterNavigate } from \"$app/navigation\";\nimport { createGuidely, destroyGuidely, guidely, setGuidelyContext, type GuidelyClient, type GuidelyOptions } from \"@guidelytech/svelte\";\nimport { onDestroy } from \"svelte\";\n\nexport * from \"@guidelytech/svelte\";\n\n/**\n * Call once from the root `+layout.svelte` (during component initialisation). Creates the client\n * in the browser only, calls `page()` after every SvelteKit navigation and destroys the client\n * when the layout unmounts (HMR). Returns `null` on the server. Keep the signed-in user in step\n * with `syncGuidelyUser` (re-exported from `@guidelytech/svelte`).\n */\nexport function initGuidely(options: GuidelyOptions): GuidelyClient | null {\n if (!browser) {\n setGuidelyContext(null);\n return null;\n }\n const client = createGuidely(options);\n setGuidelyContext(client);\n afterNavigate((nav) => {\n if (nav.type !== \"enter\") client.page(window.location.href);\n });\n onDestroy(() => {\n if (guidelyStoreIs(client)) destroyGuidely();\n });\n return client;\n}\n\nfunction guidelyStoreIs(client: GuidelyClient): boolean {\n let current: GuidelyClient | null = null;\n guidely.subscribe((c) => (current = c))();\n return current === client;\n}\n\n/**\n * Reads the public Guidely variables into client options: `VITE_GUIDELY_*` (from `import.meta.env`)\n * or SvelteKit's `PUBLIC_GUIDELY_*` (from `$env/static/public` or `$env/dynamic/public`).\n */\nexport function guidelyEnv(env: Record<string, string | undefined>): Pick<GuidelyOptions, \"projectId\" | \"environment\" | \"apiUrl\" | \"release\"> {\n return {\n projectId: env.VITE_GUIDELY_PROJECT_ID || env.PUBLIC_GUIDELY_PROJECT_ID || \"\",\n environment: ((env.VITE_GUIDELY_ENV || env.PUBLIC_GUIDELY_ENV) as GuidelyOptions[\"environment\"]) || undefined,\n apiUrl: env.VITE_GUIDELY_API_URL || env.PUBLIC_GUIDELY_API_URL || undefined,\n release: env.VITE_GUIDELY_RELEASE || env.PUBLIC_GUIDELY_RELEASE || undefined,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAwB;AACxB,wBAA8B;AAC9B,oBAAmH;AACnH,IAAAA,iBAA0B;AAE1B,0BAAc,gCALd;AAaO,SAAS,YAAY,SAA+C;AACzE,MAAI,CAAC,4BAAS;AACZ,yCAAkB,IAAI;AACtB,WAAO;AAAA,EACT;AACA,QAAM,aAAS,6BAAc,OAAO;AACpC,uCAAkB,MAAM;AACxB,uCAAc,CAAC,QAAQ;AACrB,QAAI,IAAI,SAAS,QAAS,QAAO,KAAK,OAAO,SAAS,IAAI;AAAA,EAC5D,CAAC;AACD,gCAAU,MAAM;AACd,QAAI,eAAe,MAAM,EAAG,mCAAe;AAAA,EAC7C,CAAC;AACD,SAAO;AACT;AAEA,SAAS,eAAe,QAAgC;AACtD,MAAI,UAAgC;AACpC,wBAAQ,UAAU,CAAC,MAAO,UAAU,CAAE,EAAE;AACxC,SAAO,YAAY;AACrB;AAMO,SAAS,WAAW,KAAmH;AAC5I,SAAO;AAAA,IACL,WAAW,IAAI,2BAA2B,IAAI,6BAA6B;AAAA,IAC3E,aAAe,IAAI,oBAAoB,IAAI,sBAAyD;AAAA,IACpG,QAAQ,IAAI,wBAAwB,IAAI,0BAA0B;AAAA,IAClE,SAAS,IAAI,wBAAwB,IAAI,0BAA0B;AAAA,EACrE;AACF;","names":["import_svelte"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/server.ts"],"sourcesContent":["import { browser } from \"$app/environment\";\nimport { afterNavigate } from \"$app/navigation\";\nimport { createGuidely, createStaticFlags, destroyGuidely, guidely, setFlagsContext, setGuidelyContext, type GuidelyClient, type GuidelyOptions } from \"@guidelytech/svelte\";\nimport { onDestroy } from \"svelte\";\n\nexport * from \"@guidelytech/svelte\";\n\n/**\n * Call once from the root `+layout.svelte` (during component initialisation). Creates the client\n * in the browser only, calls `page()` after every SvelteKit navigation and destroys the client\n * when the layout unmounts (HMR). Returns `null` on the server. Keep the signed-in user in step\n * with `syncGuidelyUser` (re-exported from `@guidelytech/svelte`).\n *\n * Pass `flags: data.flags` (from `loadFlags()` in `+layout.server.ts`) and the flag stores\n * render the same values on the server and in the browser.\n */\nexport function initGuidely(options: GuidelyOptions): GuidelyClient | null {\n if (!browser) {\n setGuidelyContext(null);\n // Server render: flag stores read a snapshot of the bootstrap payload.\n const flags = options.flags || undefined;\n setFlagsContext(flags?.payload ? createStaticFlags({ payload: flags.payload, attributes: flags.attributes }) : null);\n return null;\n }\n const client = createGuidely(options);\n setGuidelyContext(client);\n setFlagsContext(client);\n afterNavigate((nav) => {\n if (nav.type !== \"enter\") client.page(window.location.href);\n });\n onDestroy(() => {\n if (guidelyStoreIs(client)) destroyGuidely();\n });\n return client;\n}\n\nfunction guidelyStoreIs(client: GuidelyClient): boolean {\n let current: GuidelyClient | null = null;\n guidely.subscribe((c) => (current = c))();\n return current === client;\n}\n\n/**\n * Reads the public Guidely variables into client options: `VITE_GUIDELY_*` (from `import.meta.env`)\n * or SvelteKit's `PUBLIC_GUIDELY_*` (from `$env/static/public` or `$env/dynamic/public`).\n */\nexport function guidelyEnv(env: Record<string, string | undefined>): Pick<GuidelyOptions, \"projectId\" | \"environment\" | \"apiUrl\" | \"release\"> {\n return {\n projectId: env.VITE_GUIDELY_PROJECT_ID || env.PUBLIC_GUIDELY_PROJECT_ID || \"\",\n environment: ((env.VITE_GUIDELY_ENV || env.PUBLIC_GUIDELY_ENV) as GuidelyOptions[\"environment\"]) || undefined,\n apiUrl: env.VITE_GUIDELY_API_URL || env.PUBLIC_GUIDELY_API_URL || undefined,\n release: env.VITE_GUIDELY_RELEASE || env.PUBLIC_GUIDELY_RELEASE || undefined,\n };\n}\n\nexport { createServerFlags, getFlags, loadFlags } from \"./server\";\nexport type { LoadFlagsOptions } from \"./server\";\n","/**\n * `@guidelytech/sveltekit/server` — feature flags in `hooks.server.ts`, `+layout.server.ts`,\n * `+page.server.ts` and endpoints. No `$app` imports, no Svelte.\n *\n * ```ts\n * // src/hooks.server.ts\n * const flags = createServerFlags();\n * export const handle = async ({ event, resolve }) => {\n * event.locals.flags = flags.forUser({ id: event.locals.user?.id });\n * return resolve(event);\n * };\n * ```\n */\nimport { createFlagsClient, type Attributes, type FlagsBootstrap, type FlagsClient, type FlagsClientOptions, type FlagsEvaluator } from \"@guidelytech/flags\";\n\nexport type { Attributes, FlagsBootstrap, FlagsClient, FlagsClientOptions, FlagsEvaluator, FlagDetails, JsonValue } from \"@guidelytech/flags\";\n\ninterface Registry {\n clients: Map<string, FlagsClient>;\n current: FlagsClient | null;\n warned: Set<string>;\n}\n\n/** Kept on `globalThis` so Vite's dev-server reloads reuse the client (and its timers). */\nfunction registry(): Registry {\n const g = globalThis as { __guidelySvelteKitFlags?: Registry };\n return (g.__guidelySvelteKitFlags ??= { clients: new Map(), current: null, warned: new Set() });\n}\n\nfunction env(name: string): string | undefined {\n try {\n return (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env?.[name] || undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * The process-wide flags client (created once, reused across requests). Options default to\n * `GUIDELY_SDK_KEY` (public `pk_…` or secret `gly_sk_…`), falling back to `PUBLIC_GUIDELY_PROJECT_ID`,\n * plus `GUIDELY_API_URL` / `PUBLIC_GUIDELY_API_URL` and `GUIDELY_ENV` / `PUBLIC_GUIDELY_ENV`. Polls\n * every 60 s; on serverless / edge adapters use `{ pollingMode: \"lazy\" }`.\n */\nexport function createServerFlags(options: Partial<FlagsClientOptions> = {}): FlagsClient {\n const sdkKey = options.sdkKey ?? options.projectId ?? env(\"GUIDELY_SDK_KEY\") ?? env(\"PUBLIC_GUIDELY_PROJECT_ID\");\n if (!sdkKey) throw new Error(\"[Guidely] createServerFlags: pass `sdkKey` or set GUIDELY_SDK_KEY / PUBLIC_GUIDELY_PROJECT_ID.\");\n const apiUrl = options.apiUrl ?? env(\"GUIDELY_API_URL\") ?? env(\"PUBLIC_GUIDELY_API_URL\");\n const environment = options.environment ?? env(\"GUIDELY_ENV\") ?? env(\"PUBLIC_GUIDELY_ENV\");\n const id = `${sdkKey}|${apiUrl ?? \"\"}|${environment ?? \"\"}|${options.pollingMode ?? \"auto\"}`;\n const reg = registry();\n let client = reg.clients.get(id);\n if (!client) {\n client = createFlagsClient({ storage: false, ...options, sdkKey, apiUrl, environment });\n reg.clients.set(id, client);\n }\n reg.current = client;\n return client;\n}\n\nexport interface LoadFlagsOptions {\n /** A client from `createServerFlags(...)` (default: the last one created, or one from environment variables). */\n client?: FlagsClient;\n /** How long to wait for the first download before serving fallbacks (default 1500 ms). */\n timeoutMs?: number;\n}\n\nfunction settle(client: FlagsClient, timeoutMs: number): Promise<void> {\n if (client.isReady()) return Promise.resolve();\n return Promise.race([client.ready(), new Promise<void>((r) => setTimeout(r, timeoutMs))]);\n}\n\n/** Flag getters for one user, for this request (waits briefly for the first download). */\nexport async function getFlags(attributes: Attributes = {}, options: LoadFlagsOptions = {}): Promise<FlagsEvaluator> {\n const client = options.client ?? registry().current ?? createServerFlags();\n await settle(client, options.timeoutMs ?? 1500);\n return client.forUser(attributes);\n}\n\n/**\n * For `+layout.server.ts`: the payload and attributes to hand to the browser, so the first render\n * matches the server. Pass the request `event` (attributes come from `event.locals.flags`, set in\n * `hooks.server.ts`) or the attributes. Spread the result into `initGuidely({ …, flags: data.flags })`.\n * The payload is `null` for a `gly_sk_` client (it holds server-only flags): bootstrap from a\n * public-key client instead.\n */\nexport async function loadFlags(input: { locals?: { flags?: Pick<FlagsEvaluator, \"getAttributes\"> | null } } | Attributes = {}, options: LoadFlagsOptions = {}): Promise<{ payload: FlagsBootstrap | null; attributes: Attributes }> {\n const locals = (input as { locals?: { flags?: Pick<FlagsEvaluator, \"getAttributes\"> | null } }).locals;\n const attributes = locals && typeof locals === \"object\" ? (locals.flags?.getAttributes?.() ?? {}) : (input as Attributes);\n const client = options.client ?? registry().current ?? createServerFlags();\n if (client.sdkKey.startsWith(\"gly_sk_\")) {\n const reg = registry();\n if (!reg.warned.has(client.sdkKey)) {\n reg.warned.add(client.sdkKey);\n console.warn(\"[Guidely] loadFlags() needs a public-key client (pk_…): a gly_sk_ payload includes server-only flags. Pass { client: createServerFlags({ sdkKey: PUBLIC_GUIDELY_PROJECT_ID }) }.\");\n }\n return { payload: null, attributes };\n }\n await settle(client, options.timeoutMs ?? 1500);\n return { payload: client.getPayload(), attributes };\n}\n"],"mappings":"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","names":["import_svelte","env"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,11 +1,60 @@
|
|
|
1
1
|
import { GuidelyOptions, GuidelyClient } from '@guidelytech/svelte';
|
|
2
2
|
export * from '@guidelytech/svelte';
|
|
3
|
+
import { FlagsClient, FlagsClientOptions, Attributes, FlagsEvaluator, FlagsBootstrap } from '@guidelytech/flags';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `@guidelytech/sveltekit/server` — feature flags in `hooks.server.ts`, `+layout.server.ts`,
|
|
7
|
+
* `+page.server.ts` and endpoints. No `$app` imports, no Svelte.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* // src/hooks.server.ts
|
|
11
|
+
* const flags = createServerFlags();
|
|
12
|
+
* export const handle = async ({ event, resolve }) => {
|
|
13
|
+
* event.locals.flags = flags.forUser({ id: event.locals.user?.id });
|
|
14
|
+
* return resolve(event);
|
|
15
|
+
* };
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The process-wide flags client (created once, reused across requests). Options default to
|
|
21
|
+
* `GUIDELY_SDK_KEY` (public `pk_…` or secret `gly_sk_…`), falling back to `PUBLIC_GUIDELY_PROJECT_ID`,
|
|
22
|
+
* plus `GUIDELY_API_URL` / `PUBLIC_GUIDELY_API_URL` and `GUIDELY_ENV` / `PUBLIC_GUIDELY_ENV`. Polls
|
|
23
|
+
* every 60 s; on serverless / edge adapters use `{ pollingMode: "lazy" }`.
|
|
24
|
+
*/
|
|
25
|
+
declare function createServerFlags(options?: Partial<FlagsClientOptions>): FlagsClient;
|
|
26
|
+
interface LoadFlagsOptions {
|
|
27
|
+
/** A client from `createServerFlags(...)` (default: the last one created, or one from environment variables). */
|
|
28
|
+
client?: FlagsClient;
|
|
29
|
+
/** How long to wait for the first download before serving fallbacks (default 1500 ms). */
|
|
30
|
+
timeoutMs?: number;
|
|
31
|
+
}
|
|
32
|
+
/** Flag getters for one user, for this request (waits briefly for the first download). */
|
|
33
|
+
declare function getFlags(attributes?: Attributes, options?: LoadFlagsOptions): Promise<FlagsEvaluator>;
|
|
34
|
+
/**
|
|
35
|
+
* For `+layout.server.ts`: the payload and attributes to hand to the browser, so the first render
|
|
36
|
+
* matches the server. Pass the request `event` (attributes come from `event.locals.flags`, set in
|
|
37
|
+
* `hooks.server.ts`) or the attributes. Spread the result into `initGuidely({ …, flags: data.flags })`.
|
|
38
|
+
* The payload is `null` for a `gly_sk_` client (it holds server-only flags): bootstrap from a
|
|
39
|
+
* public-key client instead.
|
|
40
|
+
*/
|
|
41
|
+
declare function loadFlags(input?: {
|
|
42
|
+
locals?: {
|
|
43
|
+
flags?: Pick<FlagsEvaluator, "getAttributes"> | null;
|
|
44
|
+
};
|
|
45
|
+
} | Attributes, options?: LoadFlagsOptions): Promise<{
|
|
46
|
+
payload: FlagsBootstrap | null;
|
|
47
|
+
attributes: Attributes;
|
|
48
|
+
}>;
|
|
3
49
|
|
|
4
50
|
/**
|
|
5
51
|
* Call once from the root `+layout.svelte` (during component initialisation). Creates the client
|
|
6
52
|
* in the browser only, calls `page()` after every SvelteKit navigation and destroys the client
|
|
7
53
|
* when the layout unmounts (HMR). Returns `null` on the server. Keep the signed-in user in step
|
|
8
54
|
* with `syncGuidelyUser` (re-exported from `@guidelytech/svelte`).
|
|
55
|
+
*
|
|
56
|
+
* Pass `flags: data.flags` (from `loadFlags()` in `+layout.server.ts`) and the flag stores
|
|
57
|
+
* render the same values on the server and in the browser.
|
|
9
58
|
*/
|
|
10
59
|
declare function initGuidely(options: GuidelyOptions): GuidelyClient | null;
|
|
11
60
|
/**
|
|
@@ -14,4 +63,4 @@ declare function initGuidely(options: GuidelyOptions): GuidelyClient | null;
|
|
|
14
63
|
*/
|
|
15
64
|
declare function guidelyEnv(env: Record<string, string | undefined>): Pick<GuidelyOptions, "projectId" | "environment" | "apiUrl" | "release">;
|
|
16
65
|
|
|
17
|
-
export { guidelyEnv, initGuidely };
|
|
66
|
+
export { type LoadFlagsOptions, createServerFlags, getFlags, guidelyEnv, initGuidely, loadFlags };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,60 @@
|
|
|
1
1
|
import { GuidelyOptions, GuidelyClient } from '@guidelytech/svelte';
|
|
2
2
|
export * from '@guidelytech/svelte';
|
|
3
|
+
import { FlagsClient, FlagsClientOptions, Attributes, FlagsEvaluator, FlagsBootstrap } from '@guidelytech/flags';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `@guidelytech/sveltekit/server` — feature flags in `hooks.server.ts`, `+layout.server.ts`,
|
|
7
|
+
* `+page.server.ts` and endpoints. No `$app` imports, no Svelte.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* // src/hooks.server.ts
|
|
11
|
+
* const flags = createServerFlags();
|
|
12
|
+
* export const handle = async ({ event, resolve }) => {
|
|
13
|
+
* event.locals.flags = flags.forUser({ id: event.locals.user?.id });
|
|
14
|
+
* return resolve(event);
|
|
15
|
+
* };
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The process-wide flags client (created once, reused across requests). Options default to
|
|
21
|
+
* `GUIDELY_SDK_KEY` (public `pk_…` or secret `gly_sk_…`), falling back to `PUBLIC_GUIDELY_PROJECT_ID`,
|
|
22
|
+
* plus `GUIDELY_API_URL` / `PUBLIC_GUIDELY_API_URL` and `GUIDELY_ENV` / `PUBLIC_GUIDELY_ENV`. Polls
|
|
23
|
+
* every 60 s; on serverless / edge adapters use `{ pollingMode: "lazy" }`.
|
|
24
|
+
*/
|
|
25
|
+
declare function createServerFlags(options?: Partial<FlagsClientOptions>): FlagsClient;
|
|
26
|
+
interface LoadFlagsOptions {
|
|
27
|
+
/** A client from `createServerFlags(...)` (default: the last one created, or one from environment variables). */
|
|
28
|
+
client?: FlagsClient;
|
|
29
|
+
/** How long to wait for the first download before serving fallbacks (default 1500 ms). */
|
|
30
|
+
timeoutMs?: number;
|
|
31
|
+
}
|
|
32
|
+
/** Flag getters for one user, for this request (waits briefly for the first download). */
|
|
33
|
+
declare function getFlags(attributes?: Attributes, options?: LoadFlagsOptions): Promise<FlagsEvaluator>;
|
|
34
|
+
/**
|
|
35
|
+
* For `+layout.server.ts`: the payload and attributes to hand to the browser, so the first render
|
|
36
|
+
* matches the server. Pass the request `event` (attributes come from `event.locals.flags`, set in
|
|
37
|
+
* `hooks.server.ts`) or the attributes. Spread the result into `initGuidely({ …, flags: data.flags })`.
|
|
38
|
+
* The payload is `null` for a `gly_sk_` client (it holds server-only flags): bootstrap from a
|
|
39
|
+
* public-key client instead.
|
|
40
|
+
*/
|
|
41
|
+
declare function loadFlags(input?: {
|
|
42
|
+
locals?: {
|
|
43
|
+
flags?: Pick<FlagsEvaluator, "getAttributes"> | null;
|
|
44
|
+
};
|
|
45
|
+
} | Attributes, options?: LoadFlagsOptions): Promise<{
|
|
46
|
+
payload: FlagsBootstrap | null;
|
|
47
|
+
attributes: Attributes;
|
|
48
|
+
}>;
|
|
3
49
|
|
|
4
50
|
/**
|
|
5
51
|
* Call once from the root `+layout.svelte` (during component initialisation). Creates the client
|
|
6
52
|
* in the browser only, calls `page()` after every SvelteKit navigation and destroys the client
|
|
7
53
|
* when the layout unmounts (HMR). Returns `null` on the server. Keep the signed-in user in step
|
|
8
54
|
* with `syncGuidelyUser` (re-exported from `@guidelytech/svelte`).
|
|
55
|
+
*
|
|
56
|
+
* Pass `flags: data.flags` (from `loadFlags()` in `+layout.server.ts`) and the flag stores
|
|
57
|
+
* render the same values on the server and in the browser.
|
|
9
58
|
*/
|
|
10
59
|
declare function initGuidely(options: GuidelyOptions): GuidelyClient | null;
|
|
11
60
|
/**
|
|
@@ -14,4 +63,4 @@ declare function initGuidely(options: GuidelyOptions): GuidelyClient | null;
|
|
|
14
63
|
*/
|
|
15
64
|
declare function guidelyEnv(env: Record<string, string | undefined>): Pick<GuidelyOptions, "projectId" | "environment" | "apiUrl" | "release">;
|
|
16
65
|
|
|
17
|
-
export { guidelyEnv, initGuidely };
|
|
66
|
+
export { type LoadFlagsOptions, createServerFlags, getFlags, guidelyEnv, initGuidely, loadFlags };
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,74 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { browser } from "$app/environment";
|
|
3
3
|
import { afterNavigate } from "$app/navigation";
|
|
4
|
-
import { createGuidely, destroyGuidely, guidely, setGuidelyContext } from "@guidelytech/svelte";
|
|
4
|
+
import { createGuidely, createStaticFlags, destroyGuidely, guidely, setFlagsContext, setGuidelyContext } from "@guidelytech/svelte";
|
|
5
5
|
import { onDestroy } from "svelte";
|
|
6
6
|
export * from "@guidelytech/svelte";
|
|
7
|
+
|
|
8
|
+
// src/server.ts
|
|
9
|
+
import { createFlagsClient } from "@guidelytech/flags";
|
|
10
|
+
function registry() {
|
|
11
|
+
const g = globalThis;
|
|
12
|
+
return g.__guidelySvelteKitFlags ?? (g.__guidelySvelteKitFlags = { clients: /* @__PURE__ */ new Map(), current: null, warned: /* @__PURE__ */ new Set() });
|
|
13
|
+
}
|
|
14
|
+
function env(name) {
|
|
15
|
+
try {
|
|
16
|
+
return globalThis.process?.env?.[name] || void 0;
|
|
17
|
+
} catch {
|
|
18
|
+
return void 0;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function createServerFlags(options = {}) {
|
|
22
|
+
const sdkKey = options.sdkKey ?? options.projectId ?? env("GUIDELY_SDK_KEY") ?? env("PUBLIC_GUIDELY_PROJECT_ID");
|
|
23
|
+
if (!sdkKey) throw new Error("[Guidely] createServerFlags: pass `sdkKey` or set GUIDELY_SDK_KEY / PUBLIC_GUIDELY_PROJECT_ID.");
|
|
24
|
+
const apiUrl = options.apiUrl ?? env("GUIDELY_API_URL") ?? env("PUBLIC_GUIDELY_API_URL");
|
|
25
|
+
const environment = options.environment ?? env("GUIDELY_ENV") ?? env("PUBLIC_GUIDELY_ENV");
|
|
26
|
+
const id = `${sdkKey}|${apiUrl ?? ""}|${environment ?? ""}|${options.pollingMode ?? "auto"}`;
|
|
27
|
+
const reg = registry();
|
|
28
|
+
let client = reg.clients.get(id);
|
|
29
|
+
if (!client) {
|
|
30
|
+
client = createFlagsClient({ storage: false, ...options, sdkKey, apiUrl, environment });
|
|
31
|
+
reg.clients.set(id, client);
|
|
32
|
+
}
|
|
33
|
+
reg.current = client;
|
|
34
|
+
return client;
|
|
35
|
+
}
|
|
36
|
+
function settle(client, timeoutMs) {
|
|
37
|
+
if (client.isReady()) return Promise.resolve();
|
|
38
|
+
return Promise.race([client.ready(), new Promise((r) => setTimeout(r, timeoutMs))]);
|
|
39
|
+
}
|
|
40
|
+
async function getFlags(attributes = {}, options = {}) {
|
|
41
|
+
const client = options.client ?? registry().current ?? createServerFlags();
|
|
42
|
+
await settle(client, options.timeoutMs ?? 1500);
|
|
43
|
+
return client.forUser(attributes);
|
|
44
|
+
}
|
|
45
|
+
async function loadFlags(input = {}, options = {}) {
|
|
46
|
+
const locals = input.locals;
|
|
47
|
+
const attributes = locals && typeof locals === "object" ? locals.flags?.getAttributes?.() ?? {} : input;
|
|
48
|
+
const client = options.client ?? registry().current ?? createServerFlags();
|
|
49
|
+
if (client.sdkKey.startsWith("gly_sk_")) {
|
|
50
|
+
const reg = registry();
|
|
51
|
+
if (!reg.warned.has(client.sdkKey)) {
|
|
52
|
+
reg.warned.add(client.sdkKey);
|
|
53
|
+
console.warn("[Guidely] loadFlags() needs a public-key client (pk_\u2026): a gly_sk_ payload includes server-only flags. Pass { client: createServerFlags({ sdkKey: PUBLIC_GUIDELY_PROJECT_ID }) }.");
|
|
54
|
+
}
|
|
55
|
+
return { payload: null, attributes };
|
|
56
|
+
}
|
|
57
|
+
await settle(client, options.timeoutMs ?? 1500);
|
|
58
|
+
return { payload: client.getPayload(), attributes };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/index.ts
|
|
7
62
|
function initGuidely(options) {
|
|
8
63
|
if (!browser) {
|
|
9
64
|
setGuidelyContext(null);
|
|
65
|
+
const flags = options.flags || void 0;
|
|
66
|
+
setFlagsContext(flags?.payload ? createStaticFlags({ payload: flags.payload, attributes: flags.attributes }) : null);
|
|
10
67
|
return null;
|
|
11
68
|
}
|
|
12
69
|
const client = createGuidely(options);
|
|
13
70
|
setGuidelyContext(client);
|
|
71
|
+
setFlagsContext(client);
|
|
14
72
|
afterNavigate((nav) => {
|
|
15
73
|
if (nav.type !== "enter") client.page(window.location.href);
|
|
16
74
|
});
|
|
@@ -24,16 +82,19 @@ function guidelyStoreIs(client) {
|
|
|
24
82
|
guidely.subscribe((c) => current = c)();
|
|
25
83
|
return current === client;
|
|
26
84
|
}
|
|
27
|
-
function guidelyEnv(
|
|
85
|
+
function guidelyEnv(env2) {
|
|
28
86
|
return {
|
|
29
|
-
projectId:
|
|
30
|
-
environment:
|
|
31
|
-
apiUrl:
|
|
32
|
-
release:
|
|
87
|
+
projectId: env2.VITE_GUIDELY_PROJECT_ID || env2.PUBLIC_GUIDELY_PROJECT_ID || "",
|
|
88
|
+
environment: env2.VITE_GUIDELY_ENV || env2.PUBLIC_GUIDELY_ENV || void 0,
|
|
89
|
+
apiUrl: env2.VITE_GUIDELY_API_URL || env2.PUBLIC_GUIDELY_API_URL || void 0,
|
|
90
|
+
release: env2.VITE_GUIDELY_RELEASE || env2.PUBLIC_GUIDELY_RELEASE || void 0
|
|
33
91
|
};
|
|
34
92
|
}
|
|
35
93
|
export {
|
|
94
|
+
createServerFlags,
|
|
95
|
+
getFlags,
|
|
36
96
|
guidelyEnv,
|
|
37
|
-
initGuidely
|
|
97
|
+
initGuidely,
|
|
98
|
+
loadFlags
|
|
38
99
|
};
|
|
39
100
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { browser } from \"$app/environment\";\nimport { afterNavigate } from \"$app/navigation\";\nimport { createGuidely, destroyGuidely, guidely, setGuidelyContext, type GuidelyClient, type GuidelyOptions } from \"@guidelytech/svelte\";\nimport { onDestroy } from \"svelte\";\n\nexport * from \"@guidelytech/svelte\";\n\n/**\n * Call once from the root `+layout.svelte` (during component initialisation). Creates the client\n * in the browser only, calls `page()` after every SvelteKit navigation and destroys the client\n * when the layout unmounts (HMR). Returns `null` on the server. Keep the signed-in user in step\n * with `syncGuidelyUser` (re-exported from `@guidelytech/svelte`).\n */\nexport function initGuidely(options: GuidelyOptions): GuidelyClient | null {\n if (!browser) {\n setGuidelyContext(null);\n return null;\n }\n const client = createGuidely(options);\n setGuidelyContext(client);\n afterNavigate((nav) => {\n if (nav.type !== \"enter\") client.page(window.location.href);\n });\n onDestroy(() => {\n if (guidelyStoreIs(client)) destroyGuidely();\n });\n return client;\n}\n\nfunction guidelyStoreIs(client: GuidelyClient): boolean {\n let current: GuidelyClient | null = null;\n guidely.subscribe((c) => (current = c))();\n return current === client;\n}\n\n/**\n * Reads the public Guidely variables into client options: `VITE_GUIDELY_*` (from `import.meta.env`)\n * or SvelteKit's `PUBLIC_GUIDELY_*` (from `$env/static/public` or `$env/dynamic/public`).\n */\nexport function guidelyEnv(env: Record<string, string | undefined>): Pick<GuidelyOptions, \"projectId\" | \"environment\" | \"apiUrl\" | \"release\"> {\n return {\n projectId: env.VITE_GUIDELY_PROJECT_ID || env.PUBLIC_GUIDELY_PROJECT_ID || \"\",\n environment: ((env.VITE_GUIDELY_ENV || env.PUBLIC_GUIDELY_ENV) as GuidelyOptions[\"environment\"]) || undefined,\n apiUrl: env.VITE_GUIDELY_API_URL || env.PUBLIC_GUIDELY_API_URL || undefined,\n release: env.VITE_GUIDELY_RELEASE || env.PUBLIC_GUIDELY_RELEASE || undefined,\n };\n}\n"],"mappings":";AAAA,SAAS,eAAe;AACxB,SAAS,qBAAqB;AAC9B,SAAS,eAAe,gBAAgB,SAAS,yBAAkE;AACnH,SAAS,iBAAiB;AAE1B,cAAc;AAQP,SAAS,YAAY,SAA+C;AACzE,MAAI,CAAC,SAAS;AACZ,sBAAkB,IAAI;AACtB,WAAO;AAAA,EACT;AACA,QAAM,SAAS,cAAc,OAAO;AACpC,oBAAkB,MAAM;AACxB,gBAAc,CAAC,QAAQ;AACrB,QAAI,IAAI,SAAS,QAAS,QAAO,KAAK,OAAO,SAAS,IAAI;AAAA,EAC5D,CAAC;AACD,YAAU,MAAM;AACd,QAAI,eAAe,MAAM,EAAG,gBAAe;AAAA,EAC7C,CAAC;AACD,SAAO;AACT;AAEA,SAAS,eAAe,QAAgC;AACtD,MAAI,UAAgC;AACpC,UAAQ,UAAU,CAAC,MAAO,UAAU,CAAE,EAAE;AACxC,SAAO,YAAY;AACrB;AAMO,SAAS,WAAW,KAAmH;AAC5I,SAAO;AAAA,IACL,WAAW,IAAI,2BAA2B,IAAI,6BAA6B;AAAA,IAC3E,aAAe,IAAI,oBAAoB,IAAI,sBAAyD;AAAA,IACpG,QAAQ,IAAI,wBAAwB,IAAI,0BAA0B;AAAA,IAClE,SAAS,IAAI,wBAAwB,IAAI,0BAA0B;AAAA,EACrE;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/server.ts"],"sourcesContent":["import { browser } from \"$app/environment\";\nimport { afterNavigate } from \"$app/navigation\";\nimport { createGuidely, createStaticFlags, destroyGuidely, guidely, setFlagsContext, setGuidelyContext, type GuidelyClient, type GuidelyOptions } from \"@guidelytech/svelte\";\nimport { onDestroy } from \"svelte\";\n\nexport * from \"@guidelytech/svelte\";\n\n/**\n * Call once from the root `+layout.svelte` (during component initialisation). Creates the client\n * in the browser only, calls `page()` after every SvelteKit navigation and destroys the client\n * when the layout unmounts (HMR). Returns `null` on the server. Keep the signed-in user in step\n * with `syncGuidelyUser` (re-exported from `@guidelytech/svelte`).\n *\n * Pass `flags: data.flags` (from `loadFlags()` in `+layout.server.ts`) and the flag stores\n * render the same values on the server and in the browser.\n */\nexport function initGuidely(options: GuidelyOptions): GuidelyClient | null {\n if (!browser) {\n setGuidelyContext(null);\n // Server render: flag stores read a snapshot of the bootstrap payload.\n const flags = options.flags || undefined;\n setFlagsContext(flags?.payload ? createStaticFlags({ payload: flags.payload, attributes: flags.attributes }) : null);\n return null;\n }\n const client = createGuidely(options);\n setGuidelyContext(client);\n setFlagsContext(client);\n afterNavigate((nav) => {\n if (nav.type !== \"enter\") client.page(window.location.href);\n });\n onDestroy(() => {\n if (guidelyStoreIs(client)) destroyGuidely();\n });\n return client;\n}\n\nfunction guidelyStoreIs(client: GuidelyClient): boolean {\n let current: GuidelyClient | null = null;\n guidely.subscribe((c) => (current = c))();\n return current === client;\n}\n\n/**\n * Reads the public Guidely variables into client options: `VITE_GUIDELY_*` (from `import.meta.env`)\n * or SvelteKit's `PUBLIC_GUIDELY_*` (from `$env/static/public` or `$env/dynamic/public`).\n */\nexport function guidelyEnv(env: Record<string, string | undefined>): Pick<GuidelyOptions, \"projectId\" | \"environment\" | \"apiUrl\" | \"release\"> {\n return {\n projectId: env.VITE_GUIDELY_PROJECT_ID || env.PUBLIC_GUIDELY_PROJECT_ID || \"\",\n environment: ((env.VITE_GUIDELY_ENV || env.PUBLIC_GUIDELY_ENV) as GuidelyOptions[\"environment\"]) || undefined,\n apiUrl: env.VITE_GUIDELY_API_URL || env.PUBLIC_GUIDELY_API_URL || undefined,\n release: env.VITE_GUIDELY_RELEASE || env.PUBLIC_GUIDELY_RELEASE || undefined,\n };\n}\n\nexport { createServerFlags, getFlags, loadFlags } from \"./server\";\nexport type { LoadFlagsOptions } from \"./server\";\n","/**\n * `@guidelytech/sveltekit/server` — feature flags in `hooks.server.ts`, `+layout.server.ts`,\n * `+page.server.ts` and endpoints. No `$app` imports, no Svelte.\n *\n * ```ts\n * // src/hooks.server.ts\n * const flags = createServerFlags();\n * export const handle = async ({ event, resolve }) => {\n * event.locals.flags = flags.forUser({ id: event.locals.user?.id });\n * return resolve(event);\n * };\n * ```\n */\nimport { createFlagsClient, type Attributes, type FlagsBootstrap, type FlagsClient, type FlagsClientOptions, type FlagsEvaluator } from \"@guidelytech/flags\";\n\nexport type { Attributes, FlagsBootstrap, FlagsClient, FlagsClientOptions, FlagsEvaluator, FlagDetails, JsonValue } from \"@guidelytech/flags\";\n\ninterface Registry {\n clients: Map<string, FlagsClient>;\n current: FlagsClient | null;\n warned: Set<string>;\n}\n\n/** Kept on `globalThis` so Vite's dev-server reloads reuse the client (and its timers). */\nfunction registry(): Registry {\n const g = globalThis as { __guidelySvelteKitFlags?: Registry };\n return (g.__guidelySvelteKitFlags ??= { clients: new Map(), current: null, warned: new Set() });\n}\n\nfunction env(name: string): string | undefined {\n try {\n return (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env?.[name] || undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * The process-wide flags client (created once, reused across requests). Options default to\n * `GUIDELY_SDK_KEY` (public `pk_…` or secret `gly_sk_…`), falling back to `PUBLIC_GUIDELY_PROJECT_ID`,\n * plus `GUIDELY_API_URL` / `PUBLIC_GUIDELY_API_URL` and `GUIDELY_ENV` / `PUBLIC_GUIDELY_ENV`. Polls\n * every 60 s; on serverless / edge adapters use `{ pollingMode: \"lazy\" }`.\n */\nexport function createServerFlags(options: Partial<FlagsClientOptions> = {}): FlagsClient {\n const sdkKey = options.sdkKey ?? options.projectId ?? env(\"GUIDELY_SDK_KEY\") ?? env(\"PUBLIC_GUIDELY_PROJECT_ID\");\n if (!sdkKey) throw new Error(\"[Guidely] createServerFlags: pass `sdkKey` or set GUIDELY_SDK_KEY / PUBLIC_GUIDELY_PROJECT_ID.\");\n const apiUrl = options.apiUrl ?? env(\"GUIDELY_API_URL\") ?? env(\"PUBLIC_GUIDELY_API_URL\");\n const environment = options.environment ?? env(\"GUIDELY_ENV\") ?? env(\"PUBLIC_GUIDELY_ENV\");\n const id = `${sdkKey}|${apiUrl ?? \"\"}|${environment ?? \"\"}|${options.pollingMode ?? \"auto\"}`;\n const reg = registry();\n let client = reg.clients.get(id);\n if (!client) {\n client = createFlagsClient({ storage: false, ...options, sdkKey, apiUrl, environment });\n reg.clients.set(id, client);\n }\n reg.current = client;\n return client;\n}\n\nexport interface LoadFlagsOptions {\n /** A client from `createServerFlags(...)` (default: the last one created, or one from environment variables). */\n client?: FlagsClient;\n /** How long to wait for the first download before serving fallbacks (default 1500 ms). */\n timeoutMs?: number;\n}\n\nfunction settle(client: FlagsClient, timeoutMs: number): Promise<void> {\n if (client.isReady()) return Promise.resolve();\n return Promise.race([client.ready(), new Promise<void>((r) => setTimeout(r, timeoutMs))]);\n}\n\n/** Flag getters for one user, for this request (waits briefly for the first download). */\nexport async function getFlags(attributes: Attributes = {}, options: LoadFlagsOptions = {}): Promise<FlagsEvaluator> {\n const client = options.client ?? registry().current ?? createServerFlags();\n await settle(client, options.timeoutMs ?? 1500);\n return client.forUser(attributes);\n}\n\n/**\n * For `+layout.server.ts`: the payload and attributes to hand to the browser, so the first render\n * matches the server. Pass the request `event` (attributes come from `event.locals.flags`, set in\n * `hooks.server.ts`) or the attributes. Spread the result into `initGuidely({ …, flags: data.flags })`.\n * The payload is `null` for a `gly_sk_` client (it holds server-only flags): bootstrap from a\n * public-key client instead.\n */\nexport async function loadFlags(input: { locals?: { flags?: Pick<FlagsEvaluator, \"getAttributes\"> | null } } | Attributes = {}, options: LoadFlagsOptions = {}): Promise<{ payload: FlagsBootstrap | null; attributes: Attributes }> {\n const locals = (input as { locals?: { flags?: Pick<FlagsEvaluator, \"getAttributes\"> | null } }).locals;\n const attributes = locals && typeof locals === \"object\" ? (locals.flags?.getAttributes?.() ?? {}) : (input as Attributes);\n const client = options.client ?? registry().current ?? createServerFlags();\n if (client.sdkKey.startsWith(\"gly_sk_\")) {\n const reg = registry();\n if (!reg.warned.has(client.sdkKey)) {\n reg.warned.add(client.sdkKey);\n console.warn(\"[Guidely] loadFlags() needs a public-key client (pk_…): a gly_sk_ payload includes server-only flags. Pass { client: createServerFlags({ sdkKey: PUBLIC_GUIDELY_PROJECT_ID }) }.\");\n }\n return { payload: null, attributes };\n }\n await settle(client, options.timeoutMs ?? 1500);\n return { payload: client.getPayload(), attributes };\n}\n"],"mappings":"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","names":["env"]}
|
package/dist/server.cjs
ADDED
|
@@ -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/server.ts
|
|
21
|
+
var server_exports = {};
|
|
22
|
+
__export(server_exports, {
|
|
23
|
+
createServerFlags: () => createServerFlags,
|
|
24
|
+
getFlags: () => getFlags,
|
|
25
|
+
loadFlags: () => loadFlags
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(server_exports);
|
|
28
|
+
var import_flags = require("@guidelytech/flags");
|
|
29
|
+
function registry() {
|
|
30
|
+
const g = globalThis;
|
|
31
|
+
return g.__guidelySvelteKitFlags ?? (g.__guidelySvelteKitFlags = { clients: /* @__PURE__ */ new Map(), current: null, warned: /* @__PURE__ */ new Set() });
|
|
32
|
+
}
|
|
33
|
+
function env(name) {
|
|
34
|
+
try {
|
|
35
|
+
return globalThis.process?.env?.[name] || void 0;
|
|
36
|
+
} catch {
|
|
37
|
+
return void 0;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function createServerFlags(options = {}) {
|
|
41
|
+
const sdkKey = options.sdkKey ?? options.projectId ?? env("GUIDELY_SDK_KEY") ?? env("PUBLIC_GUIDELY_PROJECT_ID");
|
|
42
|
+
if (!sdkKey) throw new Error("[Guidely] createServerFlags: pass `sdkKey` or set GUIDELY_SDK_KEY / PUBLIC_GUIDELY_PROJECT_ID.");
|
|
43
|
+
const apiUrl = options.apiUrl ?? env("GUIDELY_API_URL") ?? env("PUBLIC_GUIDELY_API_URL");
|
|
44
|
+
const environment = options.environment ?? env("GUIDELY_ENV") ?? env("PUBLIC_GUIDELY_ENV");
|
|
45
|
+
const id = `${sdkKey}|${apiUrl ?? ""}|${environment ?? ""}|${options.pollingMode ?? "auto"}`;
|
|
46
|
+
const reg = registry();
|
|
47
|
+
let client = reg.clients.get(id);
|
|
48
|
+
if (!client) {
|
|
49
|
+
client = (0, import_flags.createFlagsClient)({ storage: false, ...options, sdkKey, apiUrl, environment });
|
|
50
|
+
reg.clients.set(id, client);
|
|
51
|
+
}
|
|
52
|
+
reg.current = client;
|
|
53
|
+
return client;
|
|
54
|
+
}
|
|
55
|
+
function settle(client, timeoutMs) {
|
|
56
|
+
if (client.isReady()) return Promise.resolve();
|
|
57
|
+
return Promise.race([client.ready(), new Promise((r) => setTimeout(r, timeoutMs))]);
|
|
58
|
+
}
|
|
59
|
+
async function getFlags(attributes = {}, options = {}) {
|
|
60
|
+
const client = options.client ?? registry().current ?? createServerFlags();
|
|
61
|
+
await settle(client, options.timeoutMs ?? 1500);
|
|
62
|
+
return client.forUser(attributes);
|
|
63
|
+
}
|
|
64
|
+
async function loadFlags(input = {}, options = {}) {
|
|
65
|
+
const locals = input.locals;
|
|
66
|
+
const attributes = locals && typeof locals === "object" ? locals.flags?.getAttributes?.() ?? {} : input;
|
|
67
|
+
const client = options.client ?? registry().current ?? createServerFlags();
|
|
68
|
+
if (client.sdkKey.startsWith("gly_sk_")) {
|
|
69
|
+
const reg = registry();
|
|
70
|
+
if (!reg.warned.has(client.sdkKey)) {
|
|
71
|
+
reg.warned.add(client.sdkKey);
|
|
72
|
+
console.warn("[Guidely] loadFlags() needs a public-key client (pk_\u2026): a gly_sk_ payload includes server-only flags. Pass { client: createServerFlags({ sdkKey: PUBLIC_GUIDELY_PROJECT_ID }) }.");
|
|
73
|
+
}
|
|
74
|
+
return { payload: null, attributes };
|
|
75
|
+
}
|
|
76
|
+
await settle(client, options.timeoutMs ?? 1500);
|
|
77
|
+
return { payload: client.getPayload(), attributes };
|
|
78
|
+
}
|
|
79
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
80
|
+
0 && (module.exports = {
|
|
81
|
+
createServerFlags,
|
|
82
|
+
getFlags,
|
|
83
|
+
loadFlags
|
|
84
|
+
});
|
|
85
|
+
//# sourceMappingURL=server.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["/**\n * `@guidelytech/sveltekit/server` — feature flags in `hooks.server.ts`, `+layout.server.ts`,\n * `+page.server.ts` and endpoints. No `$app` imports, no Svelte.\n *\n * ```ts\n * // src/hooks.server.ts\n * const flags = createServerFlags();\n * export const handle = async ({ event, resolve }) => {\n * event.locals.flags = flags.forUser({ id: event.locals.user?.id });\n * return resolve(event);\n * };\n * ```\n */\nimport { createFlagsClient, type Attributes, type FlagsBootstrap, type FlagsClient, type FlagsClientOptions, type FlagsEvaluator } from \"@guidelytech/flags\";\n\nexport type { Attributes, FlagsBootstrap, FlagsClient, FlagsClientOptions, FlagsEvaluator, FlagDetails, JsonValue } from \"@guidelytech/flags\";\n\ninterface Registry {\n clients: Map<string, FlagsClient>;\n current: FlagsClient | null;\n warned: Set<string>;\n}\n\n/** Kept on `globalThis` so Vite's dev-server reloads reuse the client (and its timers). */\nfunction registry(): Registry {\n const g = globalThis as { __guidelySvelteKitFlags?: Registry };\n return (g.__guidelySvelteKitFlags ??= { clients: new Map(), current: null, warned: new Set() });\n}\n\nfunction env(name: string): string | undefined {\n try {\n return (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env?.[name] || undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * The process-wide flags client (created once, reused across requests). Options default to\n * `GUIDELY_SDK_KEY` (public `pk_…` or secret `gly_sk_…`), falling back to `PUBLIC_GUIDELY_PROJECT_ID`,\n * plus `GUIDELY_API_URL` / `PUBLIC_GUIDELY_API_URL` and `GUIDELY_ENV` / `PUBLIC_GUIDELY_ENV`. Polls\n * every 60 s; on serverless / edge adapters use `{ pollingMode: \"lazy\" }`.\n */\nexport function createServerFlags(options: Partial<FlagsClientOptions> = {}): FlagsClient {\n const sdkKey = options.sdkKey ?? options.projectId ?? env(\"GUIDELY_SDK_KEY\") ?? env(\"PUBLIC_GUIDELY_PROJECT_ID\");\n if (!sdkKey) throw new Error(\"[Guidely] createServerFlags: pass `sdkKey` or set GUIDELY_SDK_KEY / PUBLIC_GUIDELY_PROJECT_ID.\");\n const apiUrl = options.apiUrl ?? env(\"GUIDELY_API_URL\") ?? env(\"PUBLIC_GUIDELY_API_URL\");\n const environment = options.environment ?? env(\"GUIDELY_ENV\") ?? env(\"PUBLIC_GUIDELY_ENV\");\n const id = `${sdkKey}|${apiUrl ?? \"\"}|${environment ?? \"\"}|${options.pollingMode ?? \"auto\"}`;\n const reg = registry();\n let client = reg.clients.get(id);\n if (!client) {\n client = createFlagsClient({ storage: false, ...options, sdkKey, apiUrl, environment });\n reg.clients.set(id, client);\n }\n reg.current = client;\n return client;\n}\n\nexport interface LoadFlagsOptions {\n /** A client from `createServerFlags(...)` (default: the last one created, or one from environment variables). */\n client?: FlagsClient;\n /** How long to wait for the first download before serving fallbacks (default 1500 ms). */\n timeoutMs?: number;\n}\n\nfunction settle(client: FlagsClient, timeoutMs: number): Promise<void> {\n if (client.isReady()) return Promise.resolve();\n return Promise.race([client.ready(), new Promise<void>((r) => setTimeout(r, timeoutMs))]);\n}\n\n/** Flag getters for one user, for this request (waits briefly for the first download). */\nexport async function getFlags(attributes: Attributes = {}, options: LoadFlagsOptions = {}): Promise<FlagsEvaluator> {\n const client = options.client ?? registry().current ?? createServerFlags();\n await settle(client, options.timeoutMs ?? 1500);\n return client.forUser(attributes);\n}\n\n/**\n * For `+layout.server.ts`: the payload and attributes to hand to the browser, so the first render\n * matches the server. Pass the request `event` (attributes come from `event.locals.flags`, set in\n * `hooks.server.ts`) or the attributes. Spread the result into `initGuidely({ …, flags: data.flags })`.\n * The payload is `null` for a `gly_sk_` client (it holds server-only flags): bootstrap from a\n * public-key client instead.\n */\nexport async function loadFlags(input: { locals?: { flags?: Pick<FlagsEvaluator, \"getAttributes\"> | null } } | Attributes = {}, options: LoadFlagsOptions = {}): Promise<{ payload: FlagsBootstrap | null; attributes: Attributes }> {\n const locals = (input as { locals?: { flags?: Pick<FlagsEvaluator, \"getAttributes\"> | null } }).locals;\n const attributes = locals && typeof locals === \"object\" ? (locals.flags?.getAttributes?.() ?? {}) : (input as Attributes);\n const client = options.client ?? registry().current ?? createServerFlags();\n if (client.sdkKey.startsWith(\"gly_sk_\")) {\n const reg = registry();\n if (!reg.warned.has(client.sdkKey)) {\n reg.warned.add(client.sdkKey);\n console.warn(\"[Guidely] loadFlags() needs a public-key client (pk_…): a gly_sk_ payload includes server-only flags. Pass { client: createServerFlags({ sdkKey: PUBLIC_GUIDELY_PROJECT_ID }) }.\");\n }\n return { payload: null, attributes };\n }\n await settle(client, options.timeoutMs ?? 1500);\n return { payload: client.getPayload(), attributes };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaA,mBAAwI;AAWxI,SAAS,WAAqB;AAC5B,QAAM,IAAI;AACV,SAAQ,EAAE,4BAAF,EAAE,0BAA4B,EAAE,SAAS,oBAAI,IAAI,GAAG,SAAS,MAAM,QAAQ,oBAAI,IAAI,EAAE;AAC/F;AAEA,SAAS,IAAI,MAAkC;AAC7C,MAAI;AACF,WAAQ,WAA0E,SAAS,MAAM,IAAI,KAAK;AAAA,EAC5G,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQO,SAAS,kBAAkB,UAAuC,CAAC,GAAgB;AACxF,QAAM,SAAS,QAAQ,UAAU,QAAQ,aAAa,IAAI,iBAAiB,KAAK,IAAI,2BAA2B;AAC/G,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,gGAAgG;AAC7H,QAAM,SAAS,QAAQ,UAAU,IAAI,iBAAiB,KAAK,IAAI,wBAAwB;AACvF,QAAM,cAAc,QAAQ,eAAe,IAAI,aAAa,KAAK,IAAI,oBAAoB;AACzF,QAAM,KAAK,GAAG,MAAM,IAAI,UAAU,EAAE,IAAI,eAAe,EAAE,IAAI,QAAQ,eAAe,MAAM;AAC1F,QAAM,MAAM,SAAS;AACrB,MAAI,SAAS,IAAI,QAAQ,IAAI,EAAE;AAC/B,MAAI,CAAC,QAAQ;AACX,iBAAS,gCAAkB,EAAE,SAAS,OAAO,GAAG,SAAS,QAAQ,QAAQ,YAAY,CAAC;AACtF,QAAI,QAAQ,IAAI,IAAI,MAAM;AAAA,EAC5B;AACA,MAAI,UAAU;AACd,SAAO;AACT;AASA,SAAS,OAAO,QAAqB,WAAkC;AACrE,MAAI,OAAO,QAAQ,EAAG,QAAO,QAAQ,QAAQ;AAC7C,SAAO,QAAQ,KAAK,CAAC,OAAO,MAAM,GAAG,IAAI,QAAc,CAAC,MAAM,WAAW,GAAG,SAAS,CAAC,CAAC,CAAC;AAC1F;AAGA,eAAsB,SAAS,aAAyB,CAAC,GAAG,UAA4B,CAAC,GAA4B;AACnH,QAAM,SAAS,QAAQ,UAAU,SAAS,EAAE,WAAW,kBAAkB;AACzE,QAAM,OAAO,QAAQ,QAAQ,aAAa,IAAI;AAC9C,SAAO,OAAO,QAAQ,UAAU;AAClC;AASA,eAAsB,UAAU,QAA4F,CAAC,GAAG,UAA4B,CAAC,GAAwE;AACnO,QAAM,SAAU,MAAgF;AAChG,QAAM,aAAa,UAAU,OAAO,WAAW,WAAY,OAAO,OAAO,gBAAgB,KAAK,CAAC,IAAM;AACrG,QAAM,SAAS,QAAQ,UAAU,SAAS,EAAE,WAAW,kBAAkB;AACzE,MAAI,OAAO,OAAO,WAAW,SAAS,GAAG;AACvC,UAAM,MAAM,SAAS;AACrB,QAAI,CAAC,IAAI,OAAO,IAAI,OAAO,MAAM,GAAG;AAClC,UAAI,OAAO,IAAI,OAAO,MAAM;AAC5B,cAAQ,KAAK,uLAAkL;AAAA,IACjM;AACA,WAAO,EAAE,SAAS,MAAM,WAAW;AAAA,EACrC;AACA,QAAM,OAAO,QAAQ,QAAQ,aAAa,IAAI;AAC9C,SAAO,EAAE,SAAS,OAAO,WAAW,GAAG,WAAW;AACpD;","names":[]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { FlagsClient, FlagsClientOptions, Attributes, FlagsEvaluator, FlagsBootstrap } from '@guidelytech/flags';
|
|
2
|
+
export { Attributes, FlagDetails, FlagsBootstrap, FlagsClient, FlagsClientOptions, FlagsEvaluator, JsonValue } from '@guidelytech/flags';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `@guidelytech/sveltekit/server` — feature flags in `hooks.server.ts`, `+layout.server.ts`,
|
|
6
|
+
* `+page.server.ts` and endpoints. No `$app` imports, no Svelte.
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* // src/hooks.server.ts
|
|
10
|
+
* const flags = createServerFlags();
|
|
11
|
+
* export const handle = async ({ event, resolve }) => {
|
|
12
|
+
* event.locals.flags = flags.forUser({ id: event.locals.user?.id });
|
|
13
|
+
* return resolve(event);
|
|
14
|
+
* };
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The process-wide flags client (created once, reused across requests). Options default to
|
|
20
|
+
* `GUIDELY_SDK_KEY` (public `pk_…` or secret `gly_sk_…`), falling back to `PUBLIC_GUIDELY_PROJECT_ID`,
|
|
21
|
+
* plus `GUIDELY_API_URL` / `PUBLIC_GUIDELY_API_URL` and `GUIDELY_ENV` / `PUBLIC_GUIDELY_ENV`. Polls
|
|
22
|
+
* every 60 s; on serverless / edge adapters use `{ pollingMode: "lazy" }`.
|
|
23
|
+
*/
|
|
24
|
+
declare function createServerFlags(options?: Partial<FlagsClientOptions>): FlagsClient;
|
|
25
|
+
interface LoadFlagsOptions {
|
|
26
|
+
/** A client from `createServerFlags(...)` (default: the last one created, or one from environment variables). */
|
|
27
|
+
client?: FlagsClient;
|
|
28
|
+
/** How long to wait for the first download before serving fallbacks (default 1500 ms). */
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
}
|
|
31
|
+
/** Flag getters for one user, for this request (waits briefly for the first download). */
|
|
32
|
+
declare function getFlags(attributes?: Attributes, options?: LoadFlagsOptions): Promise<FlagsEvaluator>;
|
|
33
|
+
/**
|
|
34
|
+
* For `+layout.server.ts`: the payload and attributes to hand to the browser, so the first render
|
|
35
|
+
* matches the server. Pass the request `event` (attributes come from `event.locals.flags`, set in
|
|
36
|
+
* `hooks.server.ts`) or the attributes. Spread the result into `initGuidely({ …, flags: data.flags })`.
|
|
37
|
+
* The payload is `null` for a `gly_sk_` client (it holds server-only flags): bootstrap from a
|
|
38
|
+
* public-key client instead.
|
|
39
|
+
*/
|
|
40
|
+
declare function loadFlags(input?: {
|
|
41
|
+
locals?: {
|
|
42
|
+
flags?: Pick<FlagsEvaluator, "getAttributes"> | null;
|
|
43
|
+
};
|
|
44
|
+
} | Attributes, options?: LoadFlagsOptions): Promise<{
|
|
45
|
+
payload: FlagsBootstrap | null;
|
|
46
|
+
attributes: Attributes;
|
|
47
|
+
}>;
|
|
48
|
+
|
|
49
|
+
export { type LoadFlagsOptions, createServerFlags, getFlags, loadFlags };
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { FlagsClient, FlagsClientOptions, Attributes, FlagsEvaluator, FlagsBootstrap } from '@guidelytech/flags';
|
|
2
|
+
export { Attributes, FlagDetails, FlagsBootstrap, FlagsClient, FlagsClientOptions, FlagsEvaluator, JsonValue } from '@guidelytech/flags';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `@guidelytech/sveltekit/server` — feature flags in `hooks.server.ts`, `+layout.server.ts`,
|
|
6
|
+
* `+page.server.ts` and endpoints. No `$app` imports, no Svelte.
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* // src/hooks.server.ts
|
|
10
|
+
* const flags = createServerFlags();
|
|
11
|
+
* export const handle = async ({ event, resolve }) => {
|
|
12
|
+
* event.locals.flags = flags.forUser({ id: event.locals.user?.id });
|
|
13
|
+
* return resolve(event);
|
|
14
|
+
* };
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The process-wide flags client (created once, reused across requests). Options default to
|
|
20
|
+
* `GUIDELY_SDK_KEY` (public `pk_…` or secret `gly_sk_…`), falling back to `PUBLIC_GUIDELY_PROJECT_ID`,
|
|
21
|
+
* plus `GUIDELY_API_URL` / `PUBLIC_GUIDELY_API_URL` and `GUIDELY_ENV` / `PUBLIC_GUIDELY_ENV`. Polls
|
|
22
|
+
* every 60 s; on serverless / edge adapters use `{ pollingMode: "lazy" }`.
|
|
23
|
+
*/
|
|
24
|
+
declare function createServerFlags(options?: Partial<FlagsClientOptions>): FlagsClient;
|
|
25
|
+
interface LoadFlagsOptions {
|
|
26
|
+
/** A client from `createServerFlags(...)` (default: the last one created, or one from environment variables). */
|
|
27
|
+
client?: FlagsClient;
|
|
28
|
+
/** How long to wait for the first download before serving fallbacks (default 1500 ms). */
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
}
|
|
31
|
+
/** Flag getters for one user, for this request (waits briefly for the first download). */
|
|
32
|
+
declare function getFlags(attributes?: Attributes, options?: LoadFlagsOptions): Promise<FlagsEvaluator>;
|
|
33
|
+
/**
|
|
34
|
+
* For `+layout.server.ts`: the payload and attributes to hand to the browser, so the first render
|
|
35
|
+
* matches the server. Pass the request `event` (attributes come from `event.locals.flags`, set in
|
|
36
|
+
* `hooks.server.ts`) or the attributes. Spread the result into `initGuidely({ …, flags: data.flags })`.
|
|
37
|
+
* The payload is `null` for a `gly_sk_` client (it holds server-only flags): bootstrap from a
|
|
38
|
+
* public-key client instead.
|
|
39
|
+
*/
|
|
40
|
+
declare function loadFlags(input?: {
|
|
41
|
+
locals?: {
|
|
42
|
+
flags?: Pick<FlagsEvaluator, "getAttributes"> | null;
|
|
43
|
+
};
|
|
44
|
+
} | Attributes, options?: LoadFlagsOptions): Promise<{
|
|
45
|
+
payload: FlagsBootstrap | null;
|
|
46
|
+
attributes: Attributes;
|
|
47
|
+
}>;
|
|
48
|
+
|
|
49
|
+
export { type LoadFlagsOptions, createServerFlags, getFlags, loadFlags };
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// src/server.ts
|
|
2
|
+
import { createFlagsClient } from "@guidelytech/flags";
|
|
3
|
+
function registry() {
|
|
4
|
+
const g = globalThis;
|
|
5
|
+
return g.__guidelySvelteKitFlags ?? (g.__guidelySvelteKitFlags = { clients: /* @__PURE__ */ new Map(), current: null, warned: /* @__PURE__ */ new Set() });
|
|
6
|
+
}
|
|
7
|
+
function env(name) {
|
|
8
|
+
try {
|
|
9
|
+
return globalThis.process?.env?.[name] || void 0;
|
|
10
|
+
} catch {
|
|
11
|
+
return void 0;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function createServerFlags(options = {}) {
|
|
15
|
+
const sdkKey = options.sdkKey ?? options.projectId ?? env("GUIDELY_SDK_KEY") ?? env("PUBLIC_GUIDELY_PROJECT_ID");
|
|
16
|
+
if (!sdkKey) throw new Error("[Guidely] createServerFlags: pass `sdkKey` or set GUIDELY_SDK_KEY / PUBLIC_GUIDELY_PROJECT_ID.");
|
|
17
|
+
const apiUrl = options.apiUrl ?? env("GUIDELY_API_URL") ?? env("PUBLIC_GUIDELY_API_URL");
|
|
18
|
+
const environment = options.environment ?? env("GUIDELY_ENV") ?? env("PUBLIC_GUIDELY_ENV");
|
|
19
|
+
const id = `${sdkKey}|${apiUrl ?? ""}|${environment ?? ""}|${options.pollingMode ?? "auto"}`;
|
|
20
|
+
const reg = registry();
|
|
21
|
+
let client = reg.clients.get(id);
|
|
22
|
+
if (!client) {
|
|
23
|
+
client = createFlagsClient({ storage: false, ...options, sdkKey, apiUrl, environment });
|
|
24
|
+
reg.clients.set(id, client);
|
|
25
|
+
}
|
|
26
|
+
reg.current = client;
|
|
27
|
+
return client;
|
|
28
|
+
}
|
|
29
|
+
function settle(client, timeoutMs) {
|
|
30
|
+
if (client.isReady()) return Promise.resolve();
|
|
31
|
+
return Promise.race([client.ready(), new Promise((r) => setTimeout(r, timeoutMs))]);
|
|
32
|
+
}
|
|
33
|
+
async function getFlags(attributes = {}, options = {}) {
|
|
34
|
+
const client = options.client ?? registry().current ?? createServerFlags();
|
|
35
|
+
await settle(client, options.timeoutMs ?? 1500);
|
|
36
|
+
return client.forUser(attributes);
|
|
37
|
+
}
|
|
38
|
+
async function loadFlags(input = {}, options = {}) {
|
|
39
|
+
const locals = input.locals;
|
|
40
|
+
const attributes = locals && typeof locals === "object" ? locals.flags?.getAttributes?.() ?? {} : input;
|
|
41
|
+
const client = options.client ?? registry().current ?? createServerFlags();
|
|
42
|
+
if (client.sdkKey.startsWith("gly_sk_")) {
|
|
43
|
+
const reg = registry();
|
|
44
|
+
if (!reg.warned.has(client.sdkKey)) {
|
|
45
|
+
reg.warned.add(client.sdkKey);
|
|
46
|
+
console.warn("[Guidely] loadFlags() needs a public-key client (pk_\u2026): a gly_sk_ payload includes server-only flags. Pass { client: createServerFlags({ sdkKey: PUBLIC_GUIDELY_PROJECT_ID }) }.");
|
|
47
|
+
}
|
|
48
|
+
return { payload: null, attributes };
|
|
49
|
+
}
|
|
50
|
+
await settle(client, options.timeoutMs ?? 1500);
|
|
51
|
+
return { payload: client.getPayload(), attributes };
|
|
52
|
+
}
|
|
53
|
+
export {
|
|
54
|
+
createServerFlags,
|
|
55
|
+
getFlags,
|
|
56
|
+
loadFlags
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["/**\n * `@guidelytech/sveltekit/server` — feature flags in `hooks.server.ts`, `+layout.server.ts`,\n * `+page.server.ts` and endpoints. No `$app` imports, no Svelte.\n *\n * ```ts\n * // src/hooks.server.ts\n * const flags = createServerFlags();\n * export const handle = async ({ event, resolve }) => {\n * event.locals.flags = flags.forUser({ id: event.locals.user?.id });\n * return resolve(event);\n * };\n * ```\n */\nimport { createFlagsClient, type Attributes, type FlagsBootstrap, type FlagsClient, type FlagsClientOptions, type FlagsEvaluator } from \"@guidelytech/flags\";\n\nexport type { Attributes, FlagsBootstrap, FlagsClient, FlagsClientOptions, FlagsEvaluator, FlagDetails, JsonValue } from \"@guidelytech/flags\";\n\ninterface Registry {\n clients: Map<string, FlagsClient>;\n current: FlagsClient | null;\n warned: Set<string>;\n}\n\n/** Kept on `globalThis` so Vite's dev-server reloads reuse the client (and its timers). */\nfunction registry(): Registry {\n const g = globalThis as { __guidelySvelteKitFlags?: Registry };\n return (g.__guidelySvelteKitFlags ??= { clients: new Map(), current: null, warned: new Set() });\n}\n\nfunction env(name: string): string | undefined {\n try {\n return (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env?.[name] || undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * The process-wide flags client (created once, reused across requests). Options default to\n * `GUIDELY_SDK_KEY` (public `pk_…` or secret `gly_sk_…`), falling back to `PUBLIC_GUIDELY_PROJECT_ID`,\n * plus `GUIDELY_API_URL` / `PUBLIC_GUIDELY_API_URL` and `GUIDELY_ENV` / `PUBLIC_GUIDELY_ENV`. Polls\n * every 60 s; on serverless / edge adapters use `{ pollingMode: \"lazy\" }`.\n */\nexport function createServerFlags(options: Partial<FlagsClientOptions> = {}): FlagsClient {\n const sdkKey = options.sdkKey ?? options.projectId ?? env(\"GUIDELY_SDK_KEY\") ?? env(\"PUBLIC_GUIDELY_PROJECT_ID\");\n if (!sdkKey) throw new Error(\"[Guidely] createServerFlags: pass `sdkKey` or set GUIDELY_SDK_KEY / PUBLIC_GUIDELY_PROJECT_ID.\");\n const apiUrl = options.apiUrl ?? env(\"GUIDELY_API_URL\") ?? env(\"PUBLIC_GUIDELY_API_URL\");\n const environment = options.environment ?? env(\"GUIDELY_ENV\") ?? env(\"PUBLIC_GUIDELY_ENV\");\n const id = `${sdkKey}|${apiUrl ?? \"\"}|${environment ?? \"\"}|${options.pollingMode ?? \"auto\"}`;\n const reg = registry();\n let client = reg.clients.get(id);\n if (!client) {\n client = createFlagsClient({ storage: false, ...options, sdkKey, apiUrl, environment });\n reg.clients.set(id, client);\n }\n reg.current = client;\n return client;\n}\n\nexport interface LoadFlagsOptions {\n /** A client from `createServerFlags(...)` (default: the last one created, or one from environment variables). */\n client?: FlagsClient;\n /** How long to wait for the first download before serving fallbacks (default 1500 ms). */\n timeoutMs?: number;\n}\n\nfunction settle(client: FlagsClient, timeoutMs: number): Promise<void> {\n if (client.isReady()) return Promise.resolve();\n return Promise.race([client.ready(), new Promise<void>((r) => setTimeout(r, timeoutMs))]);\n}\n\n/** Flag getters for one user, for this request (waits briefly for the first download). */\nexport async function getFlags(attributes: Attributes = {}, options: LoadFlagsOptions = {}): Promise<FlagsEvaluator> {\n const client = options.client ?? registry().current ?? createServerFlags();\n await settle(client, options.timeoutMs ?? 1500);\n return client.forUser(attributes);\n}\n\n/**\n * For `+layout.server.ts`: the payload and attributes to hand to the browser, so the first render\n * matches the server. Pass the request `event` (attributes come from `event.locals.flags`, set in\n * `hooks.server.ts`) or the attributes. Spread the result into `initGuidely({ …, flags: data.flags })`.\n * The payload is `null` for a `gly_sk_` client (it holds server-only flags): bootstrap from a\n * public-key client instead.\n */\nexport async function loadFlags(input: { locals?: { flags?: Pick<FlagsEvaluator, \"getAttributes\"> | null } } | Attributes = {}, options: LoadFlagsOptions = {}): Promise<{ payload: FlagsBootstrap | null; attributes: Attributes }> {\n const locals = (input as { locals?: { flags?: Pick<FlagsEvaluator, \"getAttributes\"> | null } }).locals;\n const attributes = locals && typeof locals === \"object\" ? (locals.flags?.getAttributes?.() ?? {}) : (input as Attributes);\n const client = options.client ?? registry().current ?? createServerFlags();\n if (client.sdkKey.startsWith(\"gly_sk_\")) {\n const reg = registry();\n if (!reg.warned.has(client.sdkKey)) {\n reg.warned.add(client.sdkKey);\n console.warn(\"[Guidely] loadFlags() needs a public-key client (pk_…): a gly_sk_ payload includes server-only flags. Pass { client: createServerFlags({ sdkKey: PUBLIC_GUIDELY_PROJECT_ID }) }.\");\n }\n return { payload: null, attributes };\n }\n await settle(client, options.timeoutMs ?? 1500);\n return { payload: client.getPayload(), attributes };\n}\n"],"mappings":";AAaA,SAAS,yBAA+H;AAWxI,SAAS,WAAqB;AAC5B,QAAM,IAAI;AACV,SAAQ,EAAE,4BAAF,EAAE,0BAA4B,EAAE,SAAS,oBAAI,IAAI,GAAG,SAAS,MAAM,QAAQ,oBAAI,IAAI,EAAE;AAC/F;AAEA,SAAS,IAAI,MAAkC;AAC7C,MAAI;AACF,WAAQ,WAA0E,SAAS,MAAM,IAAI,KAAK;AAAA,EAC5G,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQO,SAAS,kBAAkB,UAAuC,CAAC,GAAgB;AACxF,QAAM,SAAS,QAAQ,UAAU,QAAQ,aAAa,IAAI,iBAAiB,KAAK,IAAI,2BAA2B;AAC/G,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,gGAAgG;AAC7H,QAAM,SAAS,QAAQ,UAAU,IAAI,iBAAiB,KAAK,IAAI,wBAAwB;AACvF,QAAM,cAAc,QAAQ,eAAe,IAAI,aAAa,KAAK,IAAI,oBAAoB;AACzF,QAAM,KAAK,GAAG,MAAM,IAAI,UAAU,EAAE,IAAI,eAAe,EAAE,IAAI,QAAQ,eAAe,MAAM;AAC1F,QAAM,MAAM,SAAS;AACrB,MAAI,SAAS,IAAI,QAAQ,IAAI,EAAE;AAC/B,MAAI,CAAC,QAAQ;AACX,aAAS,kBAAkB,EAAE,SAAS,OAAO,GAAG,SAAS,QAAQ,QAAQ,YAAY,CAAC;AACtF,QAAI,QAAQ,IAAI,IAAI,MAAM;AAAA,EAC5B;AACA,MAAI,UAAU;AACd,SAAO;AACT;AASA,SAAS,OAAO,QAAqB,WAAkC;AACrE,MAAI,OAAO,QAAQ,EAAG,QAAO,QAAQ,QAAQ;AAC7C,SAAO,QAAQ,KAAK,CAAC,OAAO,MAAM,GAAG,IAAI,QAAc,CAAC,MAAM,WAAW,GAAG,SAAS,CAAC,CAAC,CAAC;AAC1F;AAGA,eAAsB,SAAS,aAAyB,CAAC,GAAG,UAA4B,CAAC,GAA4B;AACnH,QAAM,SAAS,QAAQ,UAAU,SAAS,EAAE,WAAW,kBAAkB;AACzE,QAAM,OAAO,QAAQ,QAAQ,aAAa,IAAI;AAC9C,SAAO,OAAO,QAAQ,UAAU;AAClC;AASA,eAAsB,UAAU,QAA4F,CAAC,GAAG,UAA4B,CAAC,GAAwE;AACnO,QAAM,SAAU,MAAgF;AAChG,QAAM,aAAa,UAAU,OAAO,WAAW,WAAY,OAAO,OAAO,gBAAgB,KAAK,CAAC,IAAM;AACrG,QAAM,SAAS,QAAQ,UAAU,SAAS,EAAE,WAAW,kBAAkB;AACzE,MAAI,OAAO,OAAO,WAAW,SAAS,GAAG;AACvC,UAAM,MAAM,SAAS;AACrB,QAAI,CAAC,IAAI,OAAO,IAAI,OAAO,MAAM,GAAG;AAClC,UAAI,OAAO,IAAI,OAAO,MAAM;AAC5B,cAAQ,KAAK,uLAAkL;AAAA,IACjM;AACA,WAAO,EAAE,SAAS,MAAM,WAAW;AAAA,EACrC;AACA,QAAM,OAAO,QAAQ,QAAQ,aAAa,IAAI;AAC9C,SAAO,EAAE,SAAS,OAAO,WAAW,GAAG,WAAW;AACpD;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guidelytech/sveltekit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "SvelteKit helpers for Guidely",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
"types": "./dist/index.d.ts",
|
|
14
14
|
"import": "./dist/index.js",
|
|
15
15
|
"require": "./dist/index.cjs"
|
|
16
|
+
},
|
|
17
|
+
"./server": {
|
|
18
|
+
"types": "./dist/server.d.ts",
|
|
19
|
+
"import": "./dist/server.js",
|
|
20
|
+
"require": "./dist/server.cjs"
|
|
16
21
|
}
|
|
17
22
|
},
|
|
18
23
|
"files": [
|
|
@@ -20,8 +25,9 @@
|
|
|
20
25
|
"README.md"
|
|
21
26
|
],
|
|
22
27
|
"dependencies": {
|
|
23
|
-
"@guidelytech/
|
|
24
|
-
"@guidelytech/
|
|
28
|
+
"@guidelytech/flags": "0.8.0",
|
|
29
|
+
"@guidelytech/svelte": "0.8.0",
|
|
30
|
+
"@guidelytech/sdk-core": "0.8.0"
|
|
25
31
|
},
|
|
26
32
|
"devDependencies": {
|
|
27
33
|
"tsup": "^8.5.1",
|
|
@@ -59,7 +65,7 @@
|
|
|
59
65
|
"sveltekit"
|
|
60
66
|
],
|
|
61
67
|
"scripts": {
|
|
62
|
-
"build": "tsup",
|
|
68
|
+
"build": "rm -rf dist && tsup",
|
|
63
69
|
"dev": "tsup --watch",
|
|
64
70
|
"typecheck": "tsc --noEmit",
|
|
65
71
|
"test": "vitest run",
|