Skip to content

Commit 26f564e

Browse files
committed
add initial SDK library boilerplate and basic svelte LD SDK
1 parent ae2b5bb commit 26f564e

20 files changed

+611
-0
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"packages/sdk/react-universal",
1515
"packages/sdk/react-universal/example",
1616
"packages/sdk/vercel",
17+
"packages/sdk/svelte",
1718
"packages/sdk/akamai-base",
1819
"packages/sdk/akamai-base/example",
1920
"packages/sdk/akamai-edgekv",

packages/sdk/svelte/.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.DS_Store
2+
node_modules
3+
/build
4+
/dist
5+
/.svelte-kit
6+
/package
7+
.env
8+
.env.*
9+
!.env.example
10+
vite.config.js.timestamp-*
11+
12+
# Playwright
13+
/test-results

packages/sdk/svelte/package.json

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
{
2+
"name": "@launchdarkly/svelte-client-sdk",
3+
"version": "1.0.0",
4+
"description": "Svelte LaunchDarkly SDK",
5+
"homepage": "https://github.com/launchdarkly/js-core/tree/main/packages/sdk/svelte",
6+
"repository": {
7+
"type": "git",
8+
"url": "https://github.com/launchdarkly/js-core.git"
9+
},
10+
"license": "Apache-2.0",
11+
"packageManager": "[email protected]",
12+
"keywords": [
13+
"launchdarkly",
14+
"svelte"
15+
],
16+
"type": "module",
17+
"svelte": "./dist/index.js",
18+
"types": "./dist/index.d.ts",
19+
"exports": {
20+
".": {
21+
"types": "./dist/index.d.ts",
22+
"svelte": "./dist/index.js",
23+
"default": "./dist/index.js"
24+
}
25+
},
26+
"files": [
27+
"dist",
28+
"!dist/**/*.test.*",
29+
"!dist/**/*.spec.*"
30+
],
31+
"scripts": {
32+
"clean": "rimraf dist",
33+
"dev": "vite dev",
34+
"build": "vite build && npm run package",
35+
"preview": "vite preview",
36+
"package": "svelte-kit sync && svelte-package && publint",
37+
"prepublishOnly": "npm run package",
38+
"lint": "eslint . --ext .ts,.tsx",
39+
"prettier": "prettier --write '**/*.@(js|ts|tsx|json|css)' --ignore-path ../../../.prettierignore",
40+
"check": "yarn prettier && yarn lint && yarn build && yarn test",
41+
"test": "playwright test",
42+
"test:unit": "vitest",
43+
"test:unit-ui": "vitest --ui"
44+
},
45+
"peerDependencies": {
46+
"@launchdarkly/js-client-sdk-common": "^1.1.4",
47+
"@launchdarkly/node-server-sdk": "^9.4.6",
48+
"launchdarkly-js-client-sdk": "^3.4.0",
49+
"svelte": "^4.0.0"
50+
},
51+
"dependencies": {
52+
"@launchdarkly/js-client-sdk-common": "1.1.4",
53+
"esm-env": "^1.0.0"
54+
},
55+
"devDependencies": {
56+
"@playwright/test": "^1.28.1",
57+
"@sveltejs/adapter-auto": "^3.0.0",
58+
"@sveltejs/kit": "^2.0.0",
59+
"@sveltejs/package": "^2.0.0",
60+
"@sveltejs/vite-plugin-svelte": "^3.0.0",
61+
"@testing-library/svelte": "^5.2.0",
62+
"@types/jest": "^29.5.11",
63+
"@typescript-eslint/eslint-plugin": "^6.20.0",
64+
"@typescript-eslint/parser": "^6.20.0",
65+
"@vitest/ui": "^1.6.0",
66+
"eslint": "^8.45.0",
67+
"eslint-config-airbnb-base": "^15.0.0",
68+
"eslint-config-airbnb-typescript": "^17.1.0",
69+
"eslint-config-prettier": "^8.8.0",
70+
"eslint-plugin-import": "^2.27.5",
71+
"eslint-plugin-jest": "^27.6.3",
72+
"eslint-plugin-prettier": "^5.0.0",
73+
"eslint-plugin-svelte": "^2.35.1",
74+
"jsdom": "^24.0.0",
75+
"launchdarkly-js-test-helpers": "^2.2.0",
76+
"prettier": "^3.0.0",
77+
"prettier-plugin-svelte": "^3.1.2",
78+
"publint": "^0.1.9",
79+
"rimraf": "^5.0.5",
80+
"svelte": "^4.2.7",
81+
"svelte-check": "^3.6.0",
82+
"ts-jest": "^29.1.1",
83+
"ts-node": "^10.9.2",
84+
"typedoc": "0.25.0",
85+
"typescript": "5.1.6",
86+
"vite": "^5.2.6",
87+
"vitest": "^1.6.0"
88+
}
89+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { PlaywrightTestConfig } from '@playwright/test';
2+
3+
const config: PlaywrightTestConfig = {
4+
webServer: {
5+
command: 'npm run build && npm run preview',
6+
port: 4173
7+
},
8+
testDir: 'tests',
9+
testMatch: /(.+\.)?(test|spec)\.[jt]s/
10+
};
11+
12+
export default config;

packages/sdk/svelte/src/app.d.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// See https://kit.svelte.dev/docs/types#app
2+
// for information about these interfaces
3+
declare global {
4+
namespace App {
5+
// interface Error {}
6+
// interface Locals {}
7+
// interface PageData {}
8+
// interface PageState {}
9+
// interface Platform {}
10+
}
11+
}
12+
13+
export {};

packages/sdk/svelte/src/app.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
%sveltekit.head%
7+
</head>
8+
<body data-sveltekit-preload-data="hover">
9+
<div>%sveltekit.body%</div>
10+
</body>
11+
</html>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script lang="ts">
2+
import { LD, type LDFlagsValue } from './client/SvelteLDClient.js';
3+
4+
export let flag: string;
5+
export let matches: LDFlagsValue = true;
6+
7+
$: flagValue = LD.watch(flag);
8+
</script>
9+
10+
{#if $flagValue === matches}
11+
<slot name="true" />
12+
{:else}
13+
<slot name="false" />
14+
{/if}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { initialize } from 'launchdarkly-js-client-sdk';
2+
import type {
3+
LDClient,
4+
LDFlagSet,
5+
LDFlagValue,
6+
LDContext as NodeLDContext,
7+
} from 'launchdarkly-js-client-sdk';
8+
import { derived, get, type Readable, readonly, writable, type Writable } from 'svelte/store';
9+
10+
/** Client ID for LaunchDarkly */
11+
export type LDClientID = string;
12+
13+
/** Context for LaunchDarkly */
14+
export type LDContext = NodeLDContext;
15+
16+
/** Value of LaunchDarkly flags */
17+
export type LDFlagsValue = LDFlagValue;
18+
19+
/** Flags for LaunchDarkly */
20+
export type LDFlags = LDFlagSet;
21+
22+
/**
23+
* Checks if the LaunchDarkly client is initialized.
24+
* @param {LDClient | undefined} client - The LaunchDarkly client.
25+
* @throws {Error} If the client is not initialized.
26+
*/
27+
function isClientInitialized(client: LDClient | undefined): asserts client is LDClient {
28+
if (!client) {
29+
throw new Error('LaunchDarkly client not initialized');
30+
}
31+
}
32+
33+
/**
34+
* Creates a LaunchDarkly instance.
35+
* @returns {Object} The LaunchDarkly instance object.
36+
*/
37+
function createLD() {
38+
let jsSdk: LDClient | undefined;
39+
const loading = writable(true);
40+
const flagsWritable = writable<LDFlags>({});
41+
42+
/**
43+
* Initializes the LaunchDarkly client.
44+
* @param {LDClientID} clientId - The client ID.
45+
* @param {LDContext} context - The context.
46+
* @returns {Writable<boolean>} An object with the initialization status store.
47+
*/
48+
function LDInitialize(clientId: LDClientID, context: LDContext) {
49+
jsSdk = initialize(clientId, context);
50+
51+
jsSdk.waitUntilReady().then(() => {
52+
loading.set(false);
53+
flagsWritable.set(jsSdk!.allFlags());
54+
});
55+
56+
jsSdk.on('change', () => {
57+
flagsWritable.set(jsSdk!.allFlags());
58+
});
59+
60+
return {
61+
initializing: loading,
62+
};
63+
}
64+
65+
/**
66+
* Identifies the user context.
67+
* @param {LDContext} context - The user context.
68+
* @returns {Promise} A promise that resolves when the user is identified.
69+
*/
70+
async function identify(context: LDContext) {
71+
isClientInitialized(jsSdk);
72+
return jsSdk.identify(context);
73+
}
74+
75+
/**
76+
* Watches a flag for changes.
77+
* @param {string} flagKey - The key of the flag to watch.
78+
* @returns {Readable<LDFlagsValue>} A readable store of the flag value.
79+
*/
80+
const watch = (flagKey: string): Readable<LDFlagsValue> =>
81+
derived<Writable<LDFlags>, LDFlagsValue>(flagsWritable, ($flags) => $flags[flagKey]);
82+
83+
/**
84+
* Checks if a flag is on.
85+
* @param {string} flagKey - The key of the flag to check.
86+
* @returns {boolean} True if the flag is on, false otherwise.
87+
*/
88+
const isOn = (flagKey: string): boolean => {
89+
isClientInitialized(jsSdk);
90+
const currentFlags = get(flagsWritable);
91+
return !!currentFlags[flagKey];
92+
};
93+
94+
return {
95+
identify,
96+
flags: readonly(flagsWritable),
97+
initialize: LDInitialize,
98+
initializing: readonly(loading),
99+
watch,
100+
isOn,
101+
};
102+
}
103+
104+
/** The LaunchDarkly instance */
105+
export const LD = createLD();

0 commit comments

Comments
 (0)