@canarygate/sdk 0.1.0 → 0.1.1
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/README.md +11 -8
- package/dist/{canary-gate-base-D069Q1Z_.d.mts → canary-gate-base-DzuUUPOC.d.mts} +5 -2
- package/dist/{canary-gate-base-D069Q1Z_.d.ts → canary-gate-base-DzuUUPOC.d.ts} +5 -2
- package/dist/{chunk-7KAJ7OJO.mjs → chunk-ECO47H4R.mjs} +23 -6
- package/dist/client.d.mts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +28 -9
- package/dist/client.mjs +6 -4
- package/dist/server.d.mts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +29 -7
- package/dist/server.mjs +7 -2
- package/package.json +12 -21
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Feature flag client for [CanaryGate](https://github.com/rborges98/canarygate).
|
|
4
4
|
|
|
5
|
-
- **Server (Node.js)** — snapshot on `init()` + live updates via SSE,
|
|
6
|
-
- **Browser** —
|
|
5
|
+
- **Server (Node.js)** — snapshot on `init()` + live updates via SSE, auto-reconnect and heartbeat
|
|
6
|
+
- **Browser** — snapshot + polling via `pollIntervalMs`
|
|
7
7
|
|
|
8
8
|
## Install
|
|
9
9
|
|
|
@@ -19,12 +19,11 @@ npm install @canarygate/sdk
|
|
|
19
19
|
import { CanaryGate } from '@canarygate/sdk/server'
|
|
20
20
|
|
|
21
21
|
const gate = new CanaryGate('your-api-key', {
|
|
22
|
-
environment: 'production'
|
|
23
|
-
stream: true
|
|
22
|
+
environment: 'production'
|
|
24
23
|
})
|
|
25
24
|
|
|
26
25
|
await gate.init()
|
|
27
|
-
//
|
|
26
|
+
// the snapshot is fetched and SSE opens automatically
|
|
28
27
|
|
|
29
28
|
gate.disconnect() // when shutting down
|
|
30
29
|
```
|
|
@@ -70,7 +69,7 @@ Rollout evaluation hashes `userId` deterministically — the same user always ge
|
|
|
70
69
|
| -------------------- | --------- | ----------------------- | ------------------------------------------------ |
|
|
71
70
|
| `baseUrl` | `string` | `http://localhost:3001` | CanaryGate API base URL |
|
|
72
71
|
| `environment` | `string` | — | Environment to evaluate flags against |
|
|
73
|
-
| `
|
|
72
|
+
| `pollIntervalMs` | `number` | `30000` | Browser polling interval in ms. Set `0` to disable |
|
|
74
73
|
| `reconnectDelay` | `number` | `5000` | Initial SSE reconnect delay (ms) |
|
|
75
74
|
| `maxReconnectDelay` | `number` | `30000` | Reconnect delay cap, exponential backoff (ms) |
|
|
76
75
|
| `heartbeatTimeoutMs` | `number` | `65000` | Silence window before treating the stream as dead |
|
|
@@ -79,7 +78,7 @@ Rollout evaluation hashes `userId` deterministically — the same user always ge
|
|
|
79
78
|
|
|
80
79
|
| Method | Description |
|
|
81
80
|
| --------------------- | -------------------------------------------------- |
|
|
82
|
-
| `init()` | Fetches
|
|
81
|
+
| `init()` | Fetches the snapshot; the server entry opens SSE, the browser entry starts polling |
|
|
83
82
|
| `getFlag(key, ctx?)` | Evaluates one flag (`boolean` or `rollout`) |
|
|
84
83
|
| `getFlags(ctx?)` | Evaluates all cached flags |
|
|
85
84
|
| `isStale()` | Whether the last sync attempt failed |
|
|
@@ -89,9 +88,13 @@ Rollout evaluation hashes `userId` deterministically — the same user always ge
|
|
|
89
88
|
## How sync works
|
|
90
89
|
|
|
91
90
|
1. `init()` fetches a full snapshot from `/sdk/flags`.
|
|
92
|
-
2.
|
|
91
|
+
2. The server entry always opens an SSE connection that receives granular updates per change; the browser entry polls the snapshot every `pollIntervalMs`.
|
|
93
92
|
3. On disconnect, the SDK reconnects with exponential backoff and does a full resync over the snapshot endpoint.
|
|
94
93
|
|
|
94
|
+
## Entry-point guards
|
|
95
|
+
|
|
96
|
+
Each entry point is bound to its environment: the server entry throws `Error('@canarygate/sdk/server is server-only. In browsers import from "@canarygate/sdk/client" instead.')` if used where `window` exists, and the client entry throws a browser-only error if used where there is no `window`.
|
|
97
|
+
|
|
95
98
|
## License
|
|
96
99
|
|
|
97
100
|
[MIT](./LICENSE)
|
|
@@ -16,7 +16,7 @@ type FlagEvaluationContext = {
|
|
|
16
16
|
type CanaryGateOptions = {
|
|
17
17
|
baseUrl?: string;
|
|
18
18
|
environment?: string;
|
|
19
|
-
|
|
19
|
+
pollIntervalMs?: number;
|
|
20
20
|
reconnectDelay?: number;
|
|
21
21
|
maxReconnectDelay?: number;
|
|
22
22
|
heartbeatTimeoutMs?: number;
|
|
@@ -31,19 +31,20 @@ declare class CanaryGateBase {
|
|
|
31
31
|
private readonly reconnectDelay;
|
|
32
32
|
private readonly maxReconnectDelay;
|
|
33
33
|
private readonly heartbeatTimeoutMs;
|
|
34
|
+
private readonly pollIntervalMs;
|
|
34
35
|
private cache;
|
|
35
36
|
private cacheVersions;
|
|
36
37
|
private readonly anonId;
|
|
37
38
|
private streamAbortController;
|
|
38
39
|
private reconnectTimeout;
|
|
39
40
|
private heartbeatTimeout;
|
|
41
|
+
private pollTimeout;
|
|
40
42
|
private streamRetryDelay;
|
|
41
43
|
private reconnectAttempts;
|
|
42
44
|
private stale;
|
|
43
45
|
private lastSyncAt;
|
|
44
46
|
private destroyed;
|
|
45
47
|
constructor(apiKey: string, options: CanaryGateOptions | undefined, streamEnabled: boolean, anonIdFactory: () => string);
|
|
46
|
-
protected warnStreamDisabled(): void;
|
|
47
48
|
init(): Promise<void>;
|
|
48
49
|
private replaceCacheFromSnapshot;
|
|
49
50
|
private fetchFlags;
|
|
@@ -55,6 +56,8 @@ declare class CanaryGateBase {
|
|
|
55
56
|
private scheduleReconnect;
|
|
56
57
|
private consumeStream;
|
|
57
58
|
private connectStream;
|
|
59
|
+
private startPolling;
|
|
60
|
+
private stopPolling;
|
|
58
61
|
getFlag(key: string, context?: FlagEvaluationContext): FlagData | undefined;
|
|
59
62
|
getFlags(context?: FlagEvaluationContext): FlagData[];
|
|
60
63
|
isStale(): boolean;
|
|
@@ -16,7 +16,7 @@ type FlagEvaluationContext = {
|
|
|
16
16
|
type CanaryGateOptions = {
|
|
17
17
|
baseUrl?: string;
|
|
18
18
|
environment?: string;
|
|
19
|
-
|
|
19
|
+
pollIntervalMs?: number;
|
|
20
20
|
reconnectDelay?: number;
|
|
21
21
|
maxReconnectDelay?: number;
|
|
22
22
|
heartbeatTimeoutMs?: number;
|
|
@@ -31,19 +31,20 @@ declare class CanaryGateBase {
|
|
|
31
31
|
private readonly reconnectDelay;
|
|
32
32
|
private readonly maxReconnectDelay;
|
|
33
33
|
private readonly heartbeatTimeoutMs;
|
|
34
|
+
private readonly pollIntervalMs;
|
|
34
35
|
private cache;
|
|
35
36
|
private cacheVersions;
|
|
36
37
|
private readonly anonId;
|
|
37
38
|
private streamAbortController;
|
|
38
39
|
private reconnectTimeout;
|
|
39
40
|
private heartbeatTimeout;
|
|
41
|
+
private pollTimeout;
|
|
40
42
|
private streamRetryDelay;
|
|
41
43
|
private reconnectAttempts;
|
|
42
44
|
private stale;
|
|
43
45
|
private lastSyncAt;
|
|
44
46
|
private destroyed;
|
|
45
47
|
constructor(apiKey: string, options: CanaryGateOptions | undefined, streamEnabled: boolean, anonIdFactory: () => string);
|
|
46
|
-
protected warnStreamDisabled(): void;
|
|
47
48
|
init(): Promise<void>;
|
|
48
49
|
private replaceCacheFromSnapshot;
|
|
49
50
|
private fetchFlags;
|
|
@@ -55,6 +56,8 @@ declare class CanaryGateBase {
|
|
|
55
56
|
private scheduleReconnect;
|
|
56
57
|
private consumeStream;
|
|
57
58
|
private connectStream;
|
|
59
|
+
private startPolling;
|
|
60
|
+
private stopPolling;
|
|
58
61
|
getFlag(key: string, context?: FlagEvaluationContext): FlagData | undefined;
|
|
59
62
|
getFlags(context?: FlagEvaluationContext): FlagData[];
|
|
60
63
|
isStale(): boolean;
|
|
@@ -42,6 +42,7 @@ function parseSseEventBlock(block) {
|
|
|
42
42
|
// src/canary-gate-base.ts
|
|
43
43
|
var DEFAULT_MAX_RECONNECT_DELAY_MS = 3e4;
|
|
44
44
|
var DEFAULT_HEARTBEAT_TIMEOUT_MS = 65e3;
|
|
45
|
+
var DEFAULT_POLL_INTERVAL_MS = 3e4;
|
|
45
46
|
function isAbortError(error) {
|
|
46
47
|
return error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
|
|
47
48
|
}
|
|
@@ -59,6 +60,7 @@ var CanaryGateBase = class {
|
|
|
59
60
|
this.streamAbortController = null;
|
|
60
61
|
this.reconnectTimeout = null;
|
|
61
62
|
this.heartbeatTimeout = null;
|
|
63
|
+
this.pollTimeout = null;
|
|
62
64
|
this.reconnectAttempts = 0;
|
|
63
65
|
this.stale = false;
|
|
64
66
|
this.lastSyncAt = null;
|
|
@@ -74,17 +76,17 @@ var CanaryGateBase = class {
|
|
|
74
76
|
this.reconnectDelay
|
|
75
77
|
);
|
|
76
78
|
this.heartbeatTimeoutMs = options.heartbeatTimeoutMs ?? DEFAULT_HEARTBEAT_TIMEOUT_MS;
|
|
79
|
+
this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
77
80
|
this.streamRetryDelay = this.reconnectDelay;
|
|
78
81
|
this.anonId = anonIdFactory();
|
|
79
82
|
}
|
|
80
|
-
warnStreamDisabled() {
|
|
81
|
-
console.warn(
|
|
82
|
-
"[canarygate] Real-time streams (SSE) are disabled in browser environments to protect network architecture."
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
83
|
async init() {
|
|
86
84
|
await this.fetchFlags();
|
|
87
|
-
if (this.streamEnabled)
|
|
85
|
+
if (this.streamEnabled) {
|
|
86
|
+
this.connectStream();
|
|
87
|
+
} else {
|
|
88
|
+
this.startPolling();
|
|
89
|
+
}
|
|
88
90
|
}
|
|
89
91
|
replaceCacheFromSnapshot(flags, requestedAt) {
|
|
90
92
|
const nextCache = /* @__PURE__ */ new Map();
|
|
@@ -270,6 +272,20 @@ var CanaryGateBase = class {
|
|
|
270
272
|
this.streamAbortController = abortController;
|
|
271
273
|
void this.consumeStream(abortController);
|
|
272
274
|
}
|
|
275
|
+
startPolling() {
|
|
276
|
+
if (this.destroyed || this.streamEnabled || this.pollIntervalMs <= 0 || this.pollTimeout) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
this.pollTimeout = setInterval(() => {
|
|
280
|
+
void this.fetchFlags();
|
|
281
|
+
}, this.pollIntervalMs);
|
|
282
|
+
}
|
|
283
|
+
stopPolling() {
|
|
284
|
+
if (this.pollTimeout) {
|
|
285
|
+
clearInterval(this.pollTimeout);
|
|
286
|
+
this.pollTimeout = null;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
273
289
|
getFlag(key, context) {
|
|
274
290
|
const raw = this.cache.get(key);
|
|
275
291
|
if (!raw) return void 0;
|
|
@@ -298,6 +314,7 @@ var CanaryGateBase = class {
|
|
|
298
314
|
}
|
|
299
315
|
disconnect() {
|
|
300
316
|
this.destroyed = true;
|
|
317
|
+
this.stopPolling();
|
|
301
318
|
if (this.reconnectTimeout) {
|
|
302
319
|
clearTimeout(this.reconnectTimeout);
|
|
303
320
|
this.reconnectTimeout = null;
|
package/dist/client.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as CanaryGateBase, a as CanaryGateOptions } from './canary-gate-base-
|
|
1
|
+
import { C as CanaryGateBase, a as CanaryGateOptions } from './canary-gate-base-DzuUUPOC.mjs';
|
|
2
2
|
|
|
3
3
|
declare class CanaryGate extends CanaryGateBase {
|
|
4
4
|
constructor(apiKey: string, options?: CanaryGateOptions);
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as CanaryGateBase, a as CanaryGateOptions } from './canary-gate-base-
|
|
1
|
+
import { C as CanaryGateBase, a as CanaryGateOptions } from './canary-gate-base-DzuUUPOC.js';
|
|
2
2
|
|
|
3
3
|
declare class CanaryGate extends CanaryGateBase {
|
|
4
4
|
constructor(apiKey: string, options?: CanaryGateOptions);
|
package/dist/client.js
CHANGED
|
@@ -68,6 +68,7 @@ function parseSseEventBlock(block) {
|
|
|
68
68
|
// src/canary-gate-base.ts
|
|
69
69
|
var DEFAULT_MAX_RECONNECT_DELAY_MS = 3e4;
|
|
70
70
|
var DEFAULT_HEARTBEAT_TIMEOUT_MS = 65e3;
|
|
71
|
+
var DEFAULT_POLL_INTERVAL_MS = 3e4;
|
|
71
72
|
function isAbortError(error) {
|
|
72
73
|
return error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
|
|
73
74
|
}
|
|
@@ -85,6 +86,7 @@ var CanaryGateBase = class {
|
|
|
85
86
|
this.streamAbortController = null;
|
|
86
87
|
this.reconnectTimeout = null;
|
|
87
88
|
this.heartbeatTimeout = null;
|
|
89
|
+
this.pollTimeout = null;
|
|
88
90
|
this.reconnectAttempts = 0;
|
|
89
91
|
this.stale = false;
|
|
90
92
|
this.lastSyncAt = null;
|
|
@@ -100,17 +102,17 @@ var CanaryGateBase = class {
|
|
|
100
102
|
this.reconnectDelay
|
|
101
103
|
);
|
|
102
104
|
this.heartbeatTimeoutMs = options.heartbeatTimeoutMs ?? DEFAULT_HEARTBEAT_TIMEOUT_MS;
|
|
105
|
+
this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
103
106
|
this.streamRetryDelay = this.reconnectDelay;
|
|
104
107
|
this.anonId = anonIdFactory();
|
|
105
108
|
}
|
|
106
|
-
warnStreamDisabled() {
|
|
107
|
-
console.warn(
|
|
108
|
-
"[canarygate] Real-time streams (SSE) are disabled in browser environments to protect network architecture."
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
109
|
async init() {
|
|
112
110
|
await this.fetchFlags();
|
|
113
|
-
if (this.streamEnabled)
|
|
111
|
+
if (this.streamEnabled) {
|
|
112
|
+
this.connectStream();
|
|
113
|
+
} else {
|
|
114
|
+
this.startPolling();
|
|
115
|
+
}
|
|
114
116
|
}
|
|
115
117
|
replaceCacheFromSnapshot(flags, requestedAt) {
|
|
116
118
|
const nextCache = /* @__PURE__ */ new Map();
|
|
@@ -296,6 +298,20 @@ var CanaryGateBase = class {
|
|
|
296
298
|
this.streamAbortController = abortController;
|
|
297
299
|
void this.consumeStream(abortController);
|
|
298
300
|
}
|
|
301
|
+
startPolling() {
|
|
302
|
+
if (this.destroyed || this.streamEnabled || this.pollIntervalMs <= 0 || this.pollTimeout) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
this.pollTimeout = setInterval(() => {
|
|
306
|
+
void this.fetchFlags();
|
|
307
|
+
}, this.pollIntervalMs);
|
|
308
|
+
}
|
|
309
|
+
stopPolling() {
|
|
310
|
+
if (this.pollTimeout) {
|
|
311
|
+
clearInterval(this.pollTimeout);
|
|
312
|
+
this.pollTimeout = null;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
299
315
|
getFlag(key, context) {
|
|
300
316
|
const raw = this.cache.get(key);
|
|
301
317
|
if (!raw) return void 0;
|
|
@@ -324,6 +340,7 @@ var CanaryGateBase = class {
|
|
|
324
340
|
}
|
|
325
341
|
disconnect() {
|
|
326
342
|
this.destroyed = true;
|
|
343
|
+
this.stopPolling();
|
|
327
344
|
if (this.reconnectTimeout) {
|
|
328
345
|
clearTimeout(this.reconnectTimeout);
|
|
329
346
|
this.reconnectTimeout = null;
|
|
@@ -348,10 +365,12 @@ function getOrCreateAnonId() {
|
|
|
348
365
|
}
|
|
349
366
|
var CanaryGate = class extends CanaryGateBase {
|
|
350
367
|
constructor(apiKey, options = {}) {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
368
|
+
if (typeof window === "undefined") {
|
|
369
|
+
throw new Error(
|
|
370
|
+
'@canarygate/sdk/client is browser-only. On server runtimes (Node.js, Deno, Bun, Edge) import from "@canarygate/sdk/server" instead.'
|
|
371
|
+
);
|
|
354
372
|
}
|
|
373
|
+
super(apiKey, options, false, getOrCreateAnonId);
|
|
355
374
|
}
|
|
356
375
|
};
|
|
357
376
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/client.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CanaryGateBase
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ECO47H4R.mjs";
|
|
4
4
|
|
|
5
5
|
// src/client.ts
|
|
6
6
|
var ANON_ID_KEY = "__cg_anon_id__";
|
|
@@ -16,10 +16,12 @@ function getOrCreateAnonId() {
|
|
|
16
16
|
}
|
|
17
17
|
var CanaryGate = class extends CanaryGateBase {
|
|
18
18
|
constructor(apiKey, options = {}) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
if (typeof window === "undefined") {
|
|
20
|
+
throw new Error(
|
|
21
|
+
'@canarygate/sdk/client is browser-only. On server runtimes (Node.js, Deno, Bun, Edge) import from "@canarygate/sdk/server" instead.'
|
|
22
|
+
);
|
|
22
23
|
}
|
|
24
|
+
super(apiKey, options, false, getOrCreateAnonId);
|
|
23
25
|
}
|
|
24
26
|
};
|
|
25
27
|
export {
|
package/dist/server.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as CanaryGateBase, a as CanaryGateOptions } from './canary-gate-base-
|
|
1
|
+
import { C as CanaryGateBase, a as CanaryGateOptions } from './canary-gate-base-DzuUUPOC.mjs';
|
|
2
2
|
|
|
3
3
|
declare class CanaryGate extends CanaryGateBase {
|
|
4
4
|
constructor(apiKey: string, options?: CanaryGateOptions);
|
package/dist/server.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as CanaryGateBase, a as CanaryGateOptions } from './canary-gate-base-
|
|
1
|
+
import { C as CanaryGateBase, a as CanaryGateOptions } from './canary-gate-base-DzuUUPOC.js';
|
|
2
2
|
|
|
3
3
|
declare class CanaryGate extends CanaryGateBase {
|
|
4
4
|
constructor(apiKey: string, options?: CanaryGateOptions);
|
package/dist/server.js
CHANGED
|
@@ -68,6 +68,7 @@ function parseSseEventBlock(block) {
|
|
|
68
68
|
// src/canary-gate-base.ts
|
|
69
69
|
var DEFAULT_MAX_RECONNECT_DELAY_MS = 3e4;
|
|
70
70
|
var DEFAULT_HEARTBEAT_TIMEOUT_MS = 65e3;
|
|
71
|
+
var DEFAULT_POLL_INTERVAL_MS = 3e4;
|
|
71
72
|
function isAbortError(error) {
|
|
72
73
|
return error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
|
|
73
74
|
}
|
|
@@ -85,6 +86,7 @@ var CanaryGateBase = class {
|
|
|
85
86
|
this.streamAbortController = null;
|
|
86
87
|
this.reconnectTimeout = null;
|
|
87
88
|
this.heartbeatTimeout = null;
|
|
89
|
+
this.pollTimeout = null;
|
|
88
90
|
this.reconnectAttempts = 0;
|
|
89
91
|
this.stale = false;
|
|
90
92
|
this.lastSyncAt = null;
|
|
@@ -100,17 +102,17 @@ var CanaryGateBase = class {
|
|
|
100
102
|
this.reconnectDelay
|
|
101
103
|
);
|
|
102
104
|
this.heartbeatTimeoutMs = options.heartbeatTimeoutMs ?? DEFAULT_HEARTBEAT_TIMEOUT_MS;
|
|
105
|
+
this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
103
106
|
this.streamRetryDelay = this.reconnectDelay;
|
|
104
107
|
this.anonId = anonIdFactory();
|
|
105
108
|
}
|
|
106
|
-
warnStreamDisabled() {
|
|
107
|
-
console.warn(
|
|
108
|
-
"[canarygate] Real-time streams (SSE) are disabled in browser environments to protect network architecture."
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
109
|
async init() {
|
|
112
110
|
await this.fetchFlags();
|
|
113
|
-
if (this.streamEnabled)
|
|
111
|
+
if (this.streamEnabled) {
|
|
112
|
+
this.connectStream();
|
|
113
|
+
} else {
|
|
114
|
+
this.startPolling();
|
|
115
|
+
}
|
|
114
116
|
}
|
|
115
117
|
replaceCacheFromSnapshot(flags, requestedAt) {
|
|
116
118
|
const nextCache = /* @__PURE__ */ new Map();
|
|
@@ -296,6 +298,20 @@ var CanaryGateBase = class {
|
|
|
296
298
|
this.streamAbortController = abortController;
|
|
297
299
|
void this.consumeStream(abortController);
|
|
298
300
|
}
|
|
301
|
+
startPolling() {
|
|
302
|
+
if (this.destroyed || this.streamEnabled || this.pollIntervalMs <= 0 || this.pollTimeout) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
this.pollTimeout = setInterval(() => {
|
|
306
|
+
void this.fetchFlags();
|
|
307
|
+
}, this.pollIntervalMs);
|
|
308
|
+
}
|
|
309
|
+
stopPolling() {
|
|
310
|
+
if (this.pollTimeout) {
|
|
311
|
+
clearInterval(this.pollTimeout);
|
|
312
|
+
this.pollTimeout = null;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
299
315
|
getFlag(key, context) {
|
|
300
316
|
const raw = this.cache.get(key);
|
|
301
317
|
if (!raw) return void 0;
|
|
@@ -324,6 +340,7 @@ var CanaryGateBase = class {
|
|
|
324
340
|
}
|
|
325
341
|
disconnect() {
|
|
326
342
|
this.destroyed = true;
|
|
343
|
+
this.stopPolling();
|
|
327
344
|
if (this.reconnectTimeout) {
|
|
328
345
|
clearTimeout(this.reconnectTimeout);
|
|
329
346
|
this.reconnectTimeout = null;
|
|
@@ -337,7 +354,12 @@ var CanaryGateBase = class {
|
|
|
337
354
|
// src/server.ts
|
|
338
355
|
var CanaryGate = class extends CanaryGateBase {
|
|
339
356
|
constructor(apiKey, options = {}) {
|
|
340
|
-
|
|
357
|
+
if (typeof window !== "undefined") {
|
|
358
|
+
throw new Error(
|
|
359
|
+
'@canarygate/sdk/server is server-only. In browsers import from "@canarygate/sdk/client" instead.'
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
super(apiKey, options, true, () => crypto.randomUUID());
|
|
341
363
|
}
|
|
342
364
|
};
|
|
343
365
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/server.mjs
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CanaryGateBase
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ECO47H4R.mjs";
|
|
4
4
|
|
|
5
5
|
// src/server.ts
|
|
6
6
|
var CanaryGate = class extends CanaryGateBase {
|
|
7
7
|
constructor(apiKey, options = {}) {
|
|
8
|
-
|
|
8
|
+
if (typeof window !== "undefined") {
|
|
9
|
+
throw new Error(
|
|
10
|
+
'@canarygate/sdk/server is server-only. In browsers import from "@canarygate/sdk/client" instead.'
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
super(apiKey, options, true, () => crypto.randomUUID());
|
|
9
14
|
}
|
|
10
15
|
};
|
|
11
16
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canarygate/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "CanaryGate SDK — feature flags with real-time SSE streaming",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./client": {
|
|
@@ -15,20 +15,18 @@
|
|
|
15
15
|
},
|
|
16
16
|
"./package.json": "./package.json"
|
|
17
17
|
},
|
|
18
|
-
"files": [
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
|
|
18
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup src/client.ts src/server.ts --format esm,cjs --dts --out-dir dist",
|
|
21
|
+
"check-types": "tsc --noEmit",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"test:coverage": "vitest run --coverage",
|
|
25
|
+
"prepublishOnly": "pnpm build"
|
|
26
|
+
},
|
|
23
27
|
"sideEffects": false,
|
|
24
28
|
"license": "MIT",
|
|
25
|
-
"keywords": [
|
|
26
|
-
"feature-flags",
|
|
27
|
-
"feature-toggles",
|
|
28
|
-
"sse",
|
|
29
|
-
"real-time",
|
|
30
|
-
"rollout"
|
|
31
|
-
],
|
|
29
|
+
"keywords": ["feature-flags", "feature-toggles", "sse", "real-time", "rollout"],
|
|
32
30
|
"repository": {
|
|
33
31
|
"type": "git",
|
|
34
32
|
"url": "git+https://github.com/rborges98/canarygate.git",
|
|
@@ -47,12 +45,5 @@
|
|
|
47
45
|
"tsup": "^8.5.1",
|
|
48
46
|
"typescript": "^5",
|
|
49
47
|
"vitest": "^3.0.0"
|
|
50
|
-
},
|
|
51
|
-
"scripts": {
|
|
52
|
-
"build": "tsup src/client.ts src/server.ts --format esm,cjs --dts --out-dir dist",
|
|
53
|
-
"check-types": "tsc --noEmit",
|
|
54
|
-
"test": "vitest run",
|
|
55
|
-
"test:watch": "vitest",
|
|
56
|
-
"test:coverage": "vitest run --coverage"
|
|
57
48
|
}
|
|
58
|
-
}
|
|
49
|
+
}
|