Skip to content

Commit

Permalink
Implement experimental support for Bun
Browse files Browse the repository at this point in the history
  • Loading branch information
adams85 committed Nov 15, 2024
1 parent bb8feae commit 8e0973f
Show file tree
Hide file tree
Showing 13 changed files with 279 additions and 10,813 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/js-sdk-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,26 @@ jobs:
- name: Test
run: npm run test:browser:firefox

test-bun:
runs-on: ${{ matrix.os }}
strategy:
matrix:
bun-version: ["1.1.0", "latest"]
os: [ubuntu-latest, windows-latest, macOS-latest]
name: Bun ${{ matrix.bun-version }} (${{ matrix.os }}) test
steps:
- uses: actions/checkout@v4

- uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ matrix.bun-version }}

- name: Install dependencies & build
run: npm install

- name: Test
run: npm run test:bun

test-chromium-extension-chrome:
runs-on: ubuntu-latest
strategy:
Expand Down Expand Up @@ -219,6 +239,7 @@ jobs:
needs: [
check-ts-compat,
test-browser-chrome, test-browser-chromium, test-browser-firefox,
test-bun,
test-chromium-extension-chrome, test-chromium-extension-chromium,
test-cloudflare-worker,
test-deno,
Expand Down
58 changes: 39 additions & 19 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,45 @@
"/base/": "${workspaceRoot}/"
}
},
{
// This configuration needs the `oven.bun-vscode` extension to be installed.
"type": "bun",
"name": "Run tests (Bun)",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtime": "bun",
"runtimeArgs": ["--tsconfig-override ./tsconfig.mocha.bun.json"],
"program": "./node_modules/mocha/bin/mocha.js",
"args": [
"test/bun/index.ts",
"--fgrep",
"",
"--color",
"--exit",
"--timeout",
"30000"
],
"noDebug": false,
},
{
"type": "node",
"name": "Run tests (Deno)",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "deno",
"runtimeArgs": [
"run",
"--inspect",
"-A",
"test/deno/index.ts",
"--fgrep",
"",
"--timeout",
"30000"
],
"attachSimplePort": 9229,
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
Expand Down Expand Up @@ -62,25 +101,6 @@
"skipFiles": [
"<node_internals>/**/*.js"
]
},
{
"type": "node",
"name": "Run tests (Deno)",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "deno",
"runtimeArgs": [
"run",
"--inspect-brk",
"-A",
"test/deno/index.ts",
"--fgrep",
"",
"--timeout",
"30000"
],
"attachSimplePort": 9229,
"outputCapture": "std"
}
]
}
40 changes: 40 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
"exports": {
".": {
"types": {
"bun": "./lib/esm/bun/index.d.ts",
"deno": "./lib/esm/deno/index.d.ts",
"workerd": "./lib/esm/cloudflare-worker/index.d.ts",
"browser": "./lib/esm/browser/index.root.d.ts",
"import": "./lib/esm/node/index.root.d.ts",
"require": "./lib/cjs/node/index.root.d.ts"
},
"bun": "./lib/esm/bun/index.js",
"deno": "./lib/esm/deno/index.js",
"workerd": "./lib/esm/cloudflare-worker/index.js",
"browser": "./lib/esm/browser/index.root.js",
Expand All @@ -24,6 +26,10 @@
"types": "./lib/esm/browser/index.d.ts",
"import": "./lib/esm/browser/index.js"
},
"./bun": {
"types": "./lib/esm/bun/index.d.ts",
"import": "./lib/esm/bun/index.js"
},
"./chromium-extension": {
"types": "./lib/esm/chromium-extension/index.d.ts",
"import": "./lib/esm/chromium-extension/index.js"
Expand Down Expand Up @@ -51,6 +57,9 @@
"#lib/browser": {
"browser": "./lib/esm/browser/index.js"
},
"#lib/bun": {
"browser": "./lib/esm/bun/index.js"
},
"#lib/chromium-extension": {
"browser": "./lib/esm/chromium-extension/index.js"
},
Expand Down Expand Up @@ -83,12 +92,13 @@
"test:browser:chrome": "karma start karma.browser.chrome.conf.js",
"test:browser:chromium": "karma start karma.browser.chromium.conf.js",
"test:browser:firefox": "karma start karma.browser.firefox.conf.js",
"test:bun": "bun run --tsconfig-override ./tsconfig.mocha.bun.json ./node_modules/mocha/bin/mocha.js test/bun/index.ts --timeout 30000",
"test:chromium-extension:chrome": "karma start karma.chromium-extension.chrome.conf.js",
"test:chromium-extension:chromium": "karma start karma.chromium-extension.chromium.conf.js",
"test:cloudflare-worker": "webpack -c webpack.workerd.cloudflare-worker.config.js && concurrently --raw --success command-1 --kill-others \"node test/cloudflare-worker/test-run-helper/server.mjs\" \"workerd test workerd.config.capnp\"",
"test:deno": "node deno.import-map.generator.js && node deno.set-env.js deno run $DENO_NODE_MODULES_DIR -A test/deno/index.ts --timeout 30000",
"test:node": "cross-env NODE_EXTRA_CA_CERTS=./test/node/cert/testCA.pem TS_NODE_PROJECT=./tsconfig.mocha.node.json node --expose-gc --require ts-node/register node_modules/mocha/bin/_mocha 'test/node/index.ts' --exit --timeout 30000",
"test": "npm run check:ts-compat && npm run test:browser:chrome && npm run test:chromium-extension:chrome && npm run test:cloudflare-worker && npm run test:deno && npm run test:node"
"test": "npm run check:ts-compat && npm run test:browser:chrome && npm run test:chromium-extension:chrome && npm run test:cloudflare-worker && npm run test:bun && npm run test:deno && npm run test:node"
},
"keywords": [
"configcat",
Expand All @@ -112,8 +122,10 @@
"devDependencies": {
"@babel/preset-env": "^7.20.2",
"@cloudflare/workers-types": "^4.20241106.0",
"@types/bun": "^1.1.13",
"@types/chai": "~4.3.4",
"@types/chrome": "~0.0.270",
"@types/deno": "^2.0.0",
"@types/mocha": "~10.0.1",
"@types/node": "~18.19.7",
"@types/tunnel": "~0.0.7",
Expand Down
52 changes: 52 additions & 0 deletions src/bun/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { EventEmitter } from "events";
import type { IConfigCatClient } from "../ConfigCatClient";
import type { IAutoPollOptions, ILazyLoadingOptions, IManualPollOptions } from "../ConfigCatClientOptions";
import { PollingMode } from "../ConfigCatClientOptions";
import { getClient as getClientCommon } from "../index.pubternals.core";
import { NodeHttpConfigFetcher } from "../node/NodeHttpConfigFetcher";
import CONFIGCAT_SDK_VERSION from "../Version";

/* Package public API for Bun */

/**
* Returns an instance of `ConfigCatClient` for the specified SDK Key.
* @remarks This method returns a single, shared instance per each distinct SDK Key.
* That is, a new client object is created only when there is none available for the specified SDK Key.
* Otherwise, the already created instance is returned (in which case the `pollingMode` and `options` arguments are ignored).
* So, please keep in mind that when you make multiple calls to this method using the same SDK Key, you may end up with multiple references to the same client object.
* @param sdkKey SDK Key to access the ConfigCat config.
* @param pollingMode The polling mode to use.
* @param options Options for the specified polling mode.
*/
export function getClient<TMode extends PollingMode | undefined>(sdkKey: string, pollingMode?: TMode, options?: OptionsForPollingMode<TMode>): IConfigCatClient {
return getClientCommon(sdkKey, pollingMode ?? PollingMode.AutoPoll, options,
{
configFetcher: new NodeHttpConfigFetcher(),
sdkType: "ConfigCat-UnifiedJS-Bun",
sdkVersion: CONFIGCAT_SDK_VERSION,
eventEmitterFactory: () => new EventEmitter()
});
}

export { createConsoleLogger, createFlagOverridesFromMap, disposeAllClients } from "../index.pubternals.core";

/** Options used to configure the ConfigCat SDK in the case of Auto Polling mode. */
export interface IBunAutoPollOptions extends IAutoPollOptions {
}

/** Options used to configure the ConfigCat SDK in the case of Lazy Loading mode. */
export interface IBunLazyLoadingOptions extends ILazyLoadingOptions {
}

/** Options used to configure the ConfigCat SDK in the case of Manual Polling mode. */
export interface IBunManualPollOptions extends IManualPollOptions {
}

export type OptionsForPollingMode<TMode extends PollingMode | undefined> =
TMode extends PollingMode.AutoPoll ? IBunAutoPollOptions :
TMode extends PollingMode.ManualPoll ? IBunManualPollOptions :
TMode extends PollingMode.LazyLoad ? IBunLazyLoadingOptions :
TMode extends undefined ? IBunAutoPollOptions :
never;

export * from "..";
24 changes: 24 additions & 0 deletions test/bun/ClientTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { assert } from "chai";
import { FlagOverrides, IConfigCatClient, PollingMode } from "#lib";
import * as configcatClient from "#lib/bun";

describe("ConfigCatClient tests", () => {

for (const pollingMode of [PollingMode.AutoPoll, PollingMode.LazyLoad, PollingMode.ManualPoll]) {
it(`getClient() should createInstance with ${PollingMode[pollingMode]}`, () => {

const client: IConfigCatClient = configcatClient.getClient("SDKKEY-890123456789012/1234567890123456789012", pollingMode);

assert.isDefined(client);

client.dispose();
});
}

it("createFlagOverridesFromMap() should createOverrides", () => {

const overrides: FlagOverrides = configcatClient.createFlagOverridesFromMap({ test: true }, configcatClient.OverrideBehaviour.LocalOnly);

assert.isDefined(overrides);
});
});
59 changes: 59 additions & 0 deletions test/bun/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import "mocha";
import { EventEmitter } from "events";
import * as fs from "fs";
import * as glob from "glob";
import * as path from "path";
import { PlatformAbstractions, initPlatform } from "../helpers/platform";
import { normalizePathSeparator } from "../helpers/utils";
import { isTestSpec } from "../index";
import type { IBunAutoPollOptions, IBunLazyLoadingOptions, IBunManualPollOptions } from "#lib/bun";
import { getClient } from "#lib/bun";
import type { IConfigCatKernel } from "#lib/index.pubternals";
import { NodeHttpConfigFetcher } from "#lib/node/NodeHttpConfigFetcher";
import sdkVersion from "#lib/Version";

const sdkType = "ConfigCat-UnifiedJS-Bun";

type IBunOptions = IBunAutoPollOptions | IBunManualPollOptions | IBunLazyLoadingOptions;

class BunPlatform extends PlatformAbstractions<IBunAutoPollOptions, IBunManualPollOptions, IBunLazyLoadingOptions> {
constructor() {
super();
this.gc = () => {
Bun.gc(true);
return Promise.resolve();
};
}

pathJoin(...segments: string[]) { return path.join(...segments); }

readFileUtf8(path: string) { return fs.readFileSync(path, "utf8"); }

createConfigFetcher(options?: IBunOptions) { return new NodeHttpConfigFetcher(options); }

createKernel(setupKernel?: (kernel: IConfigCatKernel) => IConfigCatKernel, options?: IBunOptions) {
const kernel: IConfigCatKernel = { configFetcher: this.createConfigFetcher(options), sdkType, sdkVersion, eventEmitterFactory: () => new EventEmitter() };
return (setupKernel ?? (k => k))(kernel);
}

protected getClientImpl = getClient;
}

export const platform = new BunPlatform();

initPlatform(platform);

/* Discover and load tests */

const testDir = path.resolve(__dirname, "..");

for (const file of glob.globIterateSync(normalizePathSeparator(testDir) + "/**/*.ts", { absolute: false })) {
const [isTest, segments] = isTestSpec(file, "bun");
if (isTest) {
const fileName = segments[segments.length - 1];
segments[segments.length - 1] = path.basename(fileName, path.extname(fileName));

/* eslint-disable @typescript-eslint/no-require-imports */
require("../" + segments.join("/"));
}
}
7 changes: 7 additions & 0 deletions test/bun/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
/* Override TS compiler settings for IDEs (like VSCode) */
"extends": "../../tsconfig.json",
"compilerOptions": {
"types": ["bun", "chai", "mocha"]
}
}
1 change: 1 addition & 0 deletions tsconfig.build.esm.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
},
"files": [
"src/browser/index.root.ts",
"src/bun/index.ts",
"src/chromium-extension/index.ts",
"src/cloudflare-worker/index.ts",
"src/deno/index.ts",
Expand Down
Loading

0 comments on commit 8e0973f

Please sign in to comment.