{"id":49689791,"url":"https://github.com/quonfig/sdk-node","last_synced_at":"2026-05-10T17:01:35.128Z","repository":{"id":348026127,"uuid":"1189893826","full_name":"quonfig/sdk-node","owner":"quonfig","description":"Quonfig SDK for Node.js — feature flags, live config, and dynamic log levels","archived":false,"fork":false,"pushed_at":"2026-05-05T18:29:08.000Z","size":326,"stargazers_count":0,"open_issues_count":7,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-05T20:29:40.423Z","etag":null,"topics":["configuration","feature-flag","feature-flags","nodejs","quonfig","sdk","typescript"],"latest_commit_sha":null,"homepage":"https://quonfig.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/quonfig.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-23T19:15:51.000Z","updated_at":"2026-05-05T18:27:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/quonfig/sdk-node","commit_stats":null,"previous_names":["quonfig/sdk-node"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/quonfig/sdk-node","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quonfig%2Fsdk-node","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quonfig%2Fsdk-node/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quonfig%2Fsdk-node/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quonfig%2Fsdk-node/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/quonfig","download_url":"https://codeload.github.com/quonfig/sdk-node/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quonfig%2Fsdk-node/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32860226,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-10T13:40:02.631Z","status":"ssl_error","status_checked_at":"2026-05-10T13:40:02.145Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["configuration","feature-flag","feature-flags","nodejs","quonfig","sdk","typescript"],"created_at":"2026-05-07T13:16:38.177Z","updated_at":"2026-05-10T17:01:35.122Z","avatar_url":"https://github.com/quonfig.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @quonfig/node\n\nNode.js SDK for [Quonfig](https://quonfig.com) — Feature Flags, Live Config, and Dynamic Log Levels.\n\n\u003e **Note:** This SDK is pre-1.0 and the API is not yet stable.\n\n## Installation\n\n```bash\nnpm install @quonfig/node\n```\n\n## Quick Start\n\n```typescript\nimport { Quonfig } from \"@quonfig/node\";\n\nconst quonfig = new Quonfig({ sdkKey: \"your-sdk-key\" });\nawait quonfig.init();\n\n// Feature flags\nif (quonfig.isEnabled(\"new-dashboard\")) {\n  // show new dashboard\n}\n\n// Config values\nconst limit = quonfig.getNumber(\"rate-limit\");\nconst regions = quonfig.getStringList(\"allowed-regions\");\n\n// Context-aware evaluation\nconst value = quonfig.get(\"homepage-hero\", {\n  user: { key: \"user-123\", country: \"US\" },\n});\n\n// Bound context for repeated lookups\nconst userClient = quonfig.inContext({\n  user: { key: \"user-123\", plan: \"pro\" },\n});\nuserClient.get(\"feature-x\");\nuserClient.isEnabled(\"beta-feature\");\n\n// Clean up when done\nquonfig.close();\n```\n\n\u003e **Migrating from earlier releases:** `isFeatureEnabled` is still available as a deprecated alias\n\u003e of `isEnabled` — both behave identically. New code should prefer `isEnabled`, which matches\n\u003e `@quonfig/javascript` and `@quonfig/react`.\n\n## Options\n\n```typescript\nnew Quonfig({\n  sdkKey: \"your-sdk-key\",        // Required (or set QUONFIG_BACKEND_SDK_KEY)\n  apiUrls: [\"https://primary.quonfig.com\", \"https://secondary.quonfig.com\"],\n                                 // Ordered failover list. Defaults are derived\n                                 // from QUONFIG_DOMAIN (see below).\n  telemetryUrl: \"https://telemetry.quonfig.com\",\n                                 // Default derived from QUONFIG_DOMAIN.\n  enableSSE: true,               // Real-time updates via SSE (default: true)\n  enablePolling: false,          // Polling fallback (default: false)\n  pollInterval: 60000,           // Polling interval in ms (default: 60000)\n  initTimeout: 10000,            // Init timeout in ms (default: 10000)\n  onNoDefault: \"error\",          // \"error\" | \"warn\" | \"ignore\" (default: \"error\")\n  globalContext: { ... },        // Context applied to all evaluations\n  datadir: \"./workspace-data\",   // Load local workspace directories instead of API\n  datafile: \"./config.json\",     // Legacy local envelope path\n});\n```\n\n## Environment variables\n\n| Variable                  | Purpose                                                                                                                                             |\n| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `QUONFIG_BACKEND_SDK_KEY` | Fallback for `sdkKey` when omitted from options.                                                                                                    |\n| `QUONFIG_DOMAIN`          | Domain used to derive default `apiUrls` and `telemetryUrl`. Defaults to `quonfig.com`. Set to `quonfig-staging.com` to point everything at staging. |\n| `QUONFIG_ENVIRONMENT`     | Environment name to use in datadir mode (overridden by the `environment` option).                                                                   |\n| `QUONFIG_DEV_CONTEXT`     | When `true`, injects `quonfig-user.email` from `~/.quonfig/tokens.json`.                                                                            |\n\nResolution order for URLs (highest wins):\n\n1. Explicit `apiUrls` / `telemetryUrl` option.\n2. `QUONFIG_DOMAIN` env var (derives `https://primary.${DOMAIN}`, `https://secondary.${DOMAIN}`,\n   `https://telemetry.${DOMAIN}`).\n3. Hardcoded default `quonfig.com`.\n\n## SSE: real-time updates\n\nWhen `enableSSE: true` (the default), the SDK opens a Server-Sent Events stream to\n`https://stream.${primary}/api/v2/sse/config` and applies each pushed envelope to the in-memory\nstore. `get*` calls always read from the in-memory store, so flag reads never block on the network —\nthey continue returning the last-known values during a disconnect.\n\n### Reconnection behavior\n\nReconnection is delegated entirely to the [`eventsource`](https://www.npmjs.com/package/eventsource)\nlibrary (currently v2.x) and is **not** configurable from `@quonfig/node`. In v2.x the defaults are:\n\n- **Initial reconnect delay:** 1000ms\n- **Backoff:** none (constant delay; no exponential growth)\n- **Jitter:** none\n- **Max retries:** unlimited — the library will retry indefinitely\n- **Server-driven delay:** the server can override the delay by sending a `retry: \u003cms\u003e` field in any\n  event (per the W3C EventSource spec)\n\nIf you need different behavior, file an issue — we may add a configuration escape hatch.\n\n### Observing connection health\n\nPass `onSSEConnectionStateChange` to surface SSE lifecycle transitions to your host application\n(logging, metrics, status pages, etc.):\n\n```typescript\nconst quonfig = new Quonfig({\n  sdkKey: process.env.QUONFIG_BACKEND_SDK_KEY!,\n  onSSEConnectionStateChange: (state) =\u003e {\n    // state: \"connecting\" | \"connected\" | \"error\" | \"disconnected\"\n    metrics.gauge(\"quonfig.sse.state\", state);\n    if (state === \"error\") log.warn(\"Quonfig SSE disconnected; reconnecting…\");\n  },\n});\n```\n\nState semantics:\n\n| State          | When it fires                                                          |\n| -------------- | ---------------------------------------------------------------------- |\n| `connecting`   | `init()` has started SSE; or after an error while the library retries. |\n| `connected`    | The SSE stream is open and receiving events.                           |\n| `error`        | The transport surfaced an error. The library will auto-reconnect.      |\n| `disconnected` | `quonfig.close()` was called.                                          |\n\nThe callback is fired only on transitions — duplicate consecutive states are suppressed. During a\ndisconnect, `get*` calls keep returning the last-known config from the in-memory store.\n\n## Dynamic log levels with Winston\n\n`winston` is an optional peer dependency. Install it alongside `@quonfig/node`, then compose the\nformat:\n\n```typescript\nimport winston from \"winston\";\nimport { Quonfig } from \"@quonfig/node\";\nimport { createWinstonFormat } from \"@quonfig/node/winston\";\n\nconst quonfig = new Quonfig({\n  sdkKey: process.env.QUONFIG_BACKEND_SDK_KEY!,\n  loggerKey: \"log-level.my-app\",\n});\nawait quonfig.init();\n\nconst logger = winston.createLogger({\n  level: \"silly\", // let Winston emit everything; Quonfig decides.\n  format: winston.format.combine(\n    createWinstonFormat(quonfig, \"myapp.services.auth\"),\n    winston.format.json()\n  ),\n  transports: [new winston.transports.Console()],\n});\n\nlogger.info(\"live-controlled\"); // emits iff shouldLog says so.\n```\n\nThe `loggerPath` (second arg) is forwarded to `quonfig.shouldLog` verbatim — no normalization — so\nrules can key on whatever identifier shape you actually log (`\"com.app.Auth\"`,\n`\"MyApp::Services::Auth\"`, etc.).\n\n## Dynamic log levels with Pino\n\n`pino` is an optional peer dependency. Install it alongside `@quonfig/node`, then wire the hook:\n\n```typescript\nimport pino from \"pino\";\nimport { Quonfig } from \"@quonfig/node\";\nimport { createPinoHooks } from \"@quonfig/node/pino\";\n\nconst quonfig = new Quonfig({\n  sdkKey: process.env.QUONFIG_BACKEND_SDK_KEY!,\n  loggerKey: \"log-level.my-app\",\n});\nawait quonfig.init();\n\nconst logger = pino({\n  level: \"trace\", // let Pino emit everything; Quonfig decides.\n  hooks: createPinoHooks(quonfig, \"myapp.services.auth\"),\n});\n\nlogger.debug(\"live-controlled\");\n```\n\nBoth adapters also ship convenience constructors — `createWinstonLogger` and `createPinoLogger` —\nthat return a ready-to-use logger with the Quonfig gate already attached.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquonfig%2Fsdk-node","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fquonfig%2Fsdk-node","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquonfig%2Fsdk-node/lists"}