@dr-sentry/sdk 1.1.0 → 1.2.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.
Files changed (70) hide show
  1. package/README.md +103 -5
  2. package/dist/cjs/cache.d.ts +30 -3
  3. package/dist/cjs/cache.d.ts.map +1 -1
  4. package/dist/cjs/cache.js +32 -5
  5. package/dist/cjs/cache.js.map +1 -1
  6. package/dist/cjs/client.d.ts +55 -1
  7. package/dist/cjs/client.d.ts.map +1 -1
  8. package/dist/cjs/client.js +297 -37
  9. package/dist/cjs/client.js.map +1 -1
  10. package/dist/cjs/defaults.d.ts +58 -0
  11. package/dist/cjs/defaults.d.ts.map +1 -0
  12. package/dist/cjs/defaults.js +175 -0
  13. package/dist/cjs/defaults.js.map +1 -0
  14. package/dist/cjs/index.d.ts +2 -1
  15. package/dist/cjs/index.d.ts.map +1 -1
  16. package/dist/cjs/index.js +4 -1
  17. package/dist/cjs/index.js.map +1 -1
  18. package/dist/cjs/persistence.d.ts +26 -0
  19. package/dist/cjs/persistence.d.ts.map +1 -0
  20. package/dist/cjs/persistence.js +74 -0
  21. package/dist/cjs/persistence.js.map +1 -0
  22. package/dist/cjs/status-reporter.d.ts +42 -0
  23. package/dist/cjs/status-reporter.d.ts.map +1 -0
  24. package/dist/cjs/status-reporter.js +153 -0
  25. package/dist/cjs/status-reporter.js.map +1 -0
  26. package/dist/cjs/streaming.d.ts +10 -0
  27. package/dist/cjs/streaming.d.ts.map +1 -1
  28. package/dist/cjs/streaming.js +9 -1
  29. package/dist/cjs/streaming.js.map +1 -1
  30. package/dist/cjs/sync.d.ts +105 -0
  31. package/dist/cjs/sync.d.ts.map +1 -0
  32. package/dist/cjs/sync.js +191 -0
  33. package/dist/cjs/sync.js.map +1 -0
  34. package/dist/cjs/types.d.ts +78 -1
  35. package/dist/cjs/types.d.ts.map +1 -1
  36. package/dist/esm/cache.d.ts +30 -3
  37. package/dist/esm/cache.d.ts.map +1 -1
  38. package/dist/esm/cache.js +32 -5
  39. package/dist/esm/cache.js.map +1 -1
  40. package/dist/esm/client.d.ts +55 -1
  41. package/dist/esm/client.d.ts.map +1 -1
  42. package/dist/esm/client.js +297 -37
  43. package/dist/esm/client.js.map +1 -1
  44. package/dist/esm/defaults.d.ts +58 -0
  45. package/dist/esm/defaults.d.ts.map +1 -0
  46. package/dist/esm/defaults.js +136 -0
  47. package/dist/esm/defaults.js.map +1 -0
  48. package/dist/esm/index.d.ts +2 -1
  49. package/dist/esm/index.d.ts.map +1 -1
  50. package/dist/esm/index.js +1 -0
  51. package/dist/esm/index.js.map +1 -1
  52. package/dist/esm/persistence.d.ts +26 -0
  53. package/dist/esm/persistence.d.ts.map +1 -0
  54. package/dist/esm/persistence.js +70 -0
  55. package/dist/esm/persistence.js.map +1 -0
  56. package/dist/esm/status-reporter.d.ts +42 -0
  57. package/dist/esm/status-reporter.d.ts.map +1 -0
  58. package/dist/esm/status-reporter.js +148 -0
  59. package/dist/esm/status-reporter.js.map +1 -0
  60. package/dist/esm/streaming.d.ts +10 -0
  61. package/dist/esm/streaming.d.ts.map +1 -1
  62. package/dist/esm/streaming.js +9 -1
  63. package/dist/esm/streaming.js.map +1 -1
  64. package/dist/esm/sync.d.ts +105 -0
  65. package/dist/esm/sync.d.ts.map +1 -0
  66. package/dist/esm/sync.js +187 -0
  67. package/dist/esm/sync.js.map +1 -0
  68. package/dist/esm/types.d.ts +78 -1
  69. package/dist/esm/types.d.ts.map +1 -1
  70. package/package.json +1 -1
@@ -0,0 +1,136 @@
1
+ import * as fs from 'fs/promises';
2
+ /**
3
+ * Parse a YAMLExport JSON payload into an OfflineFlagDefault map.
4
+ *
5
+ * `environment` is the SDK's configured environment string. It is matched
6
+ * against the export's `environments[]` first by UUID, then by name
7
+ * (case-insensitive). The first match wins.
8
+ */
9
+ export function parseDefaults(payload, environment) {
10
+ if (!payload || typeof payload !== 'object') {
11
+ throw new Error('deploysentry: defaults payload must be an object');
12
+ }
13
+ const exp = payload;
14
+ if (!Array.isArray(exp.flags)) {
15
+ throw new Error('deploysentry: defaults payload missing flags[] array');
16
+ }
17
+ let envUUID = '';
18
+ if (environment && Array.isArray(exp.environments)) {
19
+ for (const e of exp.environments) {
20
+ if (e.id === environment) {
21
+ envUUID = e.id;
22
+ break;
23
+ }
24
+ }
25
+ if (!envUUID) {
26
+ for (const e of exp.environments) {
27
+ if (e.name && e.name.toLowerCase() === environment.toLowerCase()) {
28
+ envUUID = e.id;
29
+ break;
30
+ }
31
+ }
32
+ }
33
+ }
34
+ const defaults = new Map();
35
+ for (const f of exp.flags) {
36
+ defaults.set(f.key, {
37
+ key: f.key,
38
+ flagType: f.flag_type,
39
+ defaultValue: f.default_value,
40
+ envStates: { ...(f.environments ?? {}) },
41
+ });
42
+ }
43
+ return { defaults, envUUID };
44
+ }
45
+ /**
46
+ * Resolve the offline value for a flag key. Returns `undefined` when the
47
+ * flag is not in the loaded defaults. When an env override exists for the
48
+ * resolved env UUID, its `value` is returned; otherwise the flag's global
49
+ * `defaultValue` is returned.
50
+ */
51
+ export function lookupOfflineDefault(defaults, envUUID, key) {
52
+ const d = defaults.get(key);
53
+ if (!d)
54
+ return undefined;
55
+ if (envUUID) {
56
+ const es = d.envStates[envUUID];
57
+ if (es && es.value !== '') {
58
+ return { value: es.value, enabled: es.enabled, flagType: d.flagType };
59
+ }
60
+ if (es) {
61
+ // Env state exists but value is empty — use flag default with this env's enabled flag.
62
+ return { value: d.defaultValue, enabled: es.enabled, flagType: d.flagType };
63
+ }
64
+ }
65
+ return { value: d.defaultValue, enabled: true, flagType: d.flagType };
66
+ }
67
+ /**
68
+ * Read a defaults JSON file from disk and return the parsed payload.
69
+ * Accepts either raw JSON or YAML based on the file extension.
70
+ */
71
+ export async function readDefaultsFile(path) {
72
+ const data = await fs.readFile(path, 'utf-8');
73
+ if (path.endsWith('.yaml') || path.endsWith('.yml')) {
74
+ // Lazy-load js-yaml so SDK consumers who never call this don't pay
75
+ // for the dependency at startup.
76
+ const yaml = await import('js-yaml');
77
+ return yaml.load(data);
78
+ }
79
+ return JSON.parse(data);
80
+ }
81
+ /**
82
+ * Coerce an offline default raw value into the requested TypeScript type.
83
+ * Mirrors the BoolValue / StringValue / IntValue helpers' coercion rules
84
+ * so offline defaults behave identically to live results.
85
+ */
86
+ export function coerceOfflineValue(raw, requested) {
87
+ switch (requested) {
88
+ case 'bool': {
89
+ // raw may be a JSON literal ("true", "false") OR a plain string ("true").
90
+ let s = raw;
91
+ if (raw.length >= 2 && raw[0] === '"' && raw[raw.length - 1] === '"') {
92
+ s = raw.slice(1, -1);
93
+ }
94
+ const lower = s.toLowerCase();
95
+ if (lower === 'true')
96
+ return true;
97
+ if (lower === 'false')
98
+ return false;
99
+ return undefined;
100
+ }
101
+ case 'string': {
102
+ try {
103
+ const parsed = JSON.parse(raw);
104
+ if (typeof parsed === 'string')
105
+ return parsed;
106
+ }
107
+ catch {
108
+ // not JSON — treat raw as the string itself
109
+ }
110
+ return raw;
111
+ }
112
+ case 'number': {
113
+ const n = Number(raw);
114
+ if (Number.isFinite(n))
115
+ return n;
116
+ try {
117
+ const parsed = JSON.parse(raw);
118
+ if (typeof parsed === 'number' && Number.isFinite(parsed))
119
+ return parsed;
120
+ }
121
+ catch {
122
+ /* ignore */
123
+ }
124
+ return undefined;
125
+ }
126
+ case 'json': {
127
+ try {
128
+ return JSON.parse(raw);
129
+ }
130
+ catch {
131
+ return undefined;
132
+ }
133
+ }
134
+ }
135
+ }
136
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/defaults.ts"],"names":[],"mappings":"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"}
@@ -25,5 +25,6 @@ export { FlagCache } from './cache.js';
25
25
  export { FlagStreamClient } from './streaming.js';
26
26
  export { loadFlagConfig } from './file-loader.js';
27
27
  export { evaluateLocal } from './local-evaluator.js';
28
- export type { ClientOptions, EvaluationContext, EvaluationResult, Flag, FlagCategory, FlagMetadata, FlagConfig, FlagConfigFlag, FlagConfigRule, FlagConfigEnvironment, ApiError, } from './types.js';
28
+ export { StatusReporter, resolveVersion } from './status-reporter.js';
29
+ export type { ClientOptions, EvaluationContext, EvaluationResult, Flag, FlagCategory, FlagInspection, FlagSource, FlagMetadata, FlagConfig, FlagConfigFlag, FlagConfigRule, FlagConfigEnvironment, ApiError, HealthReport, } from './types.js';
29
30
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAChB,IAAI,EACJ,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,QAAQ,GACT,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnE,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAChB,IAAI,EACJ,YAAY,EACZ,cAAc,EACd,UAAU,EACV,YAAY,EACZ,UAAU,EACV,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,QAAQ,EACR,YAAY,GACb,MAAM,SAAS,CAAC"}
package/dist/esm/index.js CHANGED
@@ -25,4 +25,5 @@ export { FlagCache } from './cache.js';
25
25
  export { FlagStreamClient } from './streaming.js';
26
26
  export { loadFlagConfig } from './file-loader.js';
27
27
  export { evaluateLocal } from './local-evaluator.js';
28
+ export { StatusReporter, resolveVersion } from './status-reporter.js';
28
29
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { Flag } from './types.js';
2
+ /**
3
+ * Persists the last fully-synced flag set to a local JSON file so the SDK can
4
+ * serve a recent local copy on the next boot when the server is unreachable.
5
+ *
6
+ * Opt-in: only constructed when the caller supplies a `persistencePath`.
7
+ * Writes are atomic (temp file + rename) so a crash mid-write never corrupts
8
+ * an existing good snapshot. All failures are swallowed and surfaced via the
9
+ * optional logger — persistence is a best-effort durability aid, never a hard
10
+ * dependency of flag evaluation.
11
+ */
12
+ export declare class FlagSnapshotStore {
13
+ private readonly path;
14
+ private readonly scope;
15
+ private readonly log;
16
+ constructor(path: string, scope: string, log?: (msg: string) => void);
17
+ /**
18
+ * Load the persisted snapshot. Returns the flags if a valid snapshot exists
19
+ * for the same scope, otherwise `null` (missing file, parse error, version
20
+ * mismatch, or scope mismatch).
21
+ */
22
+ load(): Flag[] | null;
23
+ /** Atomically write the current flag set to disk. Best-effort. */
24
+ save(flags: Flag[]): void;
25
+ }
26
+ //# sourceMappingURL=persistence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../src/persistence.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAgB/B;;;;;;;;;GASG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAwB;gBAEhC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI;IAMpE;;;;OAIG;IACH,IAAI,IAAI,IAAI,EAAE,GAAG,IAAI;IAoBrB,kEAAkE;IAClE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI;CAiB1B"}
@@ -0,0 +1,70 @@
1
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'fs';
2
+ import { dirname } from 'path';
3
+ const SNAPSHOT_VERSION = 1;
4
+ /**
5
+ * Persists the last fully-synced flag set to a local JSON file so the SDK can
6
+ * serve a recent local copy on the next boot when the server is unreachable.
7
+ *
8
+ * Opt-in: only constructed when the caller supplies a `persistencePath`.
9
+ * Writes are atomic (temp file + rename) so a crash mid-write never corrupts
10
+ * an existing good snapshot. All failures are swallowed and surfaced via the
11
+ * optional logger — persistence is a best-effort durability aid, never a hard
12
+ * dependency of flag evaluation.
13
+ */
14
+ export class FlagSnapshotStore {
15
+ path;
16
+ scope;
17
+ log;
18
+ constructor(path, scope, log) {
19
+ this.path = path;
20
+ this.scope = scope;
21
+ this.log = log ?? (() => { });
22
+ }
23
+ /**
24
+ * Load the persisted snapshot. Returns the flags if a valid snapshot exists
25
+ * for the same scope, otherwise `null` (missing file, parse error, version
26
+ * mismatch, or scope mismatch).
27
+ */
28
+ load() {
29
+ let raw;
30
+ try {
31
+ raw = readFileSync(this.path, 'utf8');
32
+ }
33
+ catch {
34
+ return null; // No snapshot yet — expected on first run.
35
+ }
36
+ try {
37
+ const parsed = JSON.parse(raw);
38
+ if (parsed.version !== SNAPSHOT_VERSION)
39
+ return null;
40
+ if (parsed.scope !== this.scope)
41
+ return null;
42
+ if (!Array.isArray(parsed.flags))
43
+ return null;
44
+ return parsed.flags;
45
+ }
46
+ catch (err) {
47
+ this.log(`failed to parse snapshot ${this.path}: ${String(err)}`);
48
+ return null;
49
+ }
50
+ }
51
+ /** Atomically write the current flag set to disk. Best-effort. */
52
+ save(flags) {
53
+ const payload = {
54
+ version: SNAPSHOT_VERSION,
55
+ savedAt: new Date().toISOString(),
56
+ scope: this.scope,
57
+ flags,
58
+ };
59
+ const tmp = `${this.path}.${process.pid}.tmp`;
60
+ try {
61
+ mkdirSync(dirname(this.path), { recursive: true });
62
+ writeFileSync(tmp, JSON.stringify(payload), 'utf8');
63
+ renameSync(tmp, this.path); // Atomic on POSIX/Windows same-volume.
64
+ }
65
+ catch (err) {
66
+ this.log(`failed to persist snapshot ${this.path}: ${String(err)}`);
67
+ }
68
+ }
69
+ }
70
+ //# sourceMappingURL=persistence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistence.js","sourceRoot":"","sources":["../../src/persistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAe/B,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAE3B;;;;;;;;;GASG;AACH,MAAM,OAAO,iBAAiB;IACX,IAAI,CAAS;IACb,KAAK,CAAS;IACd,GAAG,CAAwB;IAE5C,YAAY,IAAY,EAAE,KAAa,EAAE,GAA2B;QAClE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACH,IAAI;QACF,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC,CAAC,2CAA2C;QAC1D,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAiB,CAAC;YAC/C,IAAI,MAAM,CAAC,OAAO,KAAK,gBAAgB;gBAAE,OAAO,IAAI,CAAC;YACrD,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK;gBAAE,OAAO,IAAI,CAAC;YAC7C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC9C,OAAO,MAAM,CAAC,KAAK,CAAC;QACtB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,4BAA4B,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAClE,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,kEAAkE;IAClE,IAAI,CAAC,KAAa;QAChB,MAAM,OAAO,GAAiB;YAC5B,OAAO,EAAE,gBAAgB;YACzB,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACjC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,KAAK;SACN,CAAC;QAEF,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;QAC9C,IAAI,CAAC;YACH,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACnD,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;YACpD,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,uCAAuC;QACrE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,8BAA8B,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,42 @@
1
+ import type { HealthReport } from './types.js';
2
+ /** Options passed to StatusReporter. Internal to the SDK. */
3
+ export interface StatusReporterOptions {
4
+ baseURL: string;
5
+ apiKey: string;
6
+ applicationId: string;
7
+ intervalMs?: number;
8
+ version?: string;
9
+ commitSha?: string;
10
+ deploySlot?: string;
11
+ tags?: Record<string, string>;
12
+ healthProvider?: () => HealthReport | Promise<HealthReport>;
13
+ /** Injection seam for tests. Defaults to global fetch. */
14
+ fetchImpl?: typeof fetch;
15
+ /** Injection seam for tests. */
16
+ warn?: (msg: string) => void;
17
+ }
18
+ /**
19
+ * Resolve the version the app should report, in this preference order:
20
+ * explicit config → standard env vars → `npm_package_version` → "unknown".
21
+ */
22
+ export declare function resolveVersion(explicit?: string): string;
23
+ /**
24
+ * StatusReporter posts periodic `POST /applications/:id/status` updates to
25
+ * DeploySentry on behalf of a DeploySentryClient. Failures are swallowed
26
+ * (logged via `warn`) so flag evaluation is never blocked by reporting.
27
+ */
28
+ export declare class StatusReporter {
29
+ private readonly opts;
30
+ private timer;
31
+ private stopped;
32
+ private backoff;
33
+ constructor(options: StatusReporterOptions);
34
+ /** Fire one immediate report and (if intervalMs > 0) schedule repeats. */
35
+ start(): void;
36
+ /** Stop scheduling further reports. In-flight reports still resolve. */
37
+ stop(): void;
38
+ /** Send exactly one report. Public for tests and explicit callers. */
39
+ reportOnce(): Promise<void>;
40
+ private tick;
41
+ }
42
+ //# sourceMappingURL=status-reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-reporter.d.ts","sourceRoot":"","sources":["../../src/status-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAiB5C,6DAA6D;AAC7D,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC5D,0DAA0D;IAC1D,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,gCAAgC;IAChC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CASxD;AAED;;;;GAIG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAGyE;IAE9F,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAK;gBAER,OAAO,EAAE,qBAAqB;IAiB1C,0EAA0E;IAC1E,KAAK,IAAI,IAAI;IAKb,wEAAwE;IACxE,IAAI,IAAI,IAAI;IAQZ,sEAAsE;IAChE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAuCnB,IAAI;CA+BnB"}
@@ -0,0 +1,148 @@
1
+ /** Environment variable names probed (in order) for the running version. */
2
+ const VERSION_ENV_CHAIN = [
3
+ 'APP_VERSION',
4
+ 'GIT_SHA',
5
+ 'GIT_COMMIT',
6
+ 'SOURCE_COMMIT',
7
+ 'RAILWAY_GIT_COMMIT_SHA',
8
+ 'RENDER_GIT_COMMIT',
9
+ 'VERCEL_GIT_COMMIT_SHA',
10
+ 'HEROKU_SLUG_COMMIT',
11
+ ];
12
+ const MIN_BACKOFF_MS = 1_000;
13
+ const MAX_BACKOFF_MS = 5 * 60_000;
14
+ /**
15
+ * Resolve the version the app should report, in this preference order:
16
+ * explicit config → standard env vars → `npm_package_version` → "unknown".
17
+ */
18
+ export function resolveVersion(explicit) {
19
+ if (explicit && explicit.trim())
20
+ return explicit;
21
+ const env = typeof process !== 'undefined' && process.env ? process.env : {};
22
+ for (const name of VERSION_ENV_CHAIN) {
23
+ const v = env[name];
24
+ if (v && v.trim())
25
+ return v;
26
+ }
27
+ if (env.npm_package_version)
28
+ return env.npm_package_version;
29
+ return 'unknown';
30
+ }
31
+ /**
32
+ * StatusReporter posts periodic `POST /applications/:id/status` updates to
33
+ * DeploySentry on behalf of a DeploySentryClient. Failures are swallowed
34
+ * (logged via `warn`) so flag evaluation is never blocked by reporting.
35
+ */
36
+ export class StatusReporter {
37
+ opts;
38
+ timer = null;
39
+ stopped = false;
40
+ backoff = 0;
41
+ constructor(options) {
42
+ if (!options.baseURL)
43
+ throw new Error('baseURL is required');
44
+ if (!options.apiKey)
45
+ throw new Error('apiKey is required');
46
+ if (!options.applicationId)
47
+ throw new Error('applicationId is required');
48
+ const intervalMs = options.intervalMs ?? 30_000;
49
+ if (intervalMs < 0)
50
+ throw new Error('intervalMs must be >= 0');
51
+ this.opts = {
52
+ ...options,
53
+ baseURL: options.baseURL.replace(/\/+$/, ''),
54
+ apiKey: options.apiKey,
55
+ applicationId: options.applicationId,
56
+ intervalMs,
57
+ warn: options.warn ?? ((msg) => console.warn('[DeploySentry] ' + msg)),
58
+ };
59
+ }
60
+ /** Fire one immediate report and (if intervalMs > 0) schedule repeats. */
61
+ start() {
62
+ this.stopped = false;
63
+ void this.tick();
64
+ }
65
+ /** Stop scheduling further reports. In-flight reports still resolve. */
66
+ stop() {
67
+ this.stopped = true;
68
+ if (this.timer) {
69
+ clearTimeout(this.timer);
70
+ this.timer = null;
71
+ }
72
+ }
73
+ /** Send exactly one report. Public for tests and explicit callers. */
74
+ async reportOnce() {
75
+ const version = resolveVersion(this.opts.version);
76
+ let health = { state: 'healthy' };
77
+ if (this.opts.healthProvider) {
78
+ try {
79
+ health = await this.opts.healthProvider();
80
+ }
81
+ catch (err) {
82
+ health = { state: 'unknown', reason: String(err?.message ?? err) };
83
+ }
84
+ }
85
+ const body = {
86
+ version,
87
+ health: health.state,
88
+ };
89
+ if (health.score !== undefined)
90
+ body.health_score = health.score;
91
+ if (health.reason)
92
+ body.health_reason = health.reason;
93
+ if (this.opts.commitSha)
94
+ body.commit_sha = this.opts.commitSha;
95
+ if (this.opts.deploySlot)
96
+ body.deploy_slot = this.opts.deploySlot;
97
+ if (this.opts.tags && Object.keys(this.opts.tags).length)
98
+ body.tags = this.opts.tags;
99
+ const fetchImpl = this.opts.fetchImpl ?? fetch;
100
+ const url = `${this.opts.baseURL}/api/v1/applications/${encodeURIComponent(this.opts.applicationId)}/status`;
101
+ const res = await fetchImpl(url, {
102
+ method: 'POST',
103
+ headers: {
104
+ 'Content-Type': 'application/json',
105
+ Authorization: `ApiKey ${this.opts.apiKey}`,
106
+ },
107
+ body: JSON.stringify(body),
108
+ });
109
+ if (!res.ok) {
110
+ throw new Error(`status report failed: ${res.status} ${res.statusText}`);
111
+ }
112
+ }
113
+ async tick() {
114
+ if (this.stopped)
115
+ return;
116
+ try {
117
+ await this.reportOnce();
118
+ this.backoff = 0; // success — clear backoff
119
+ }
120
+ catch (err) {
121
+ this.opts.warn(`status report error: ${err.message}`);
122
+ if (this.backoff >= MAX_BACKOFF_MS) {
123
+ // Already at the ceiling. Reset so the next schedule falls back to
124
+ // intervalMs — otherwise a server that recovers mid-outage takes up
125
+ // to MAX_BACKOFF_MS (5m) to be noticed, regardless of how tight the
126
+ // operator configured intervalMs. On the next failure the 1s ladder
127
+ // restarts, so the short-outage behavior (quickly re-probe while
128
+ // the server is still down) is preserved.
129
+ this.backoff = 0;
130
+ this.opts.warn(`status reporter backoff reset; probing every ${this.opts.intervalMs}ms`);
131
+ }
132
+ else {
133
+ this.backoff = this.backoff === 0 ? MIN_BACKOFF_MS : Math.min(this.backoff * 2, MAX_BACKOFF_MS);
134
+ }
135
+ }
136
+ if (this.stopped)
137
+ return;
138
+ if (this.opts.intervalMs === 0)
139
+ return; // startup-only
140
+ const nextDelay = this.backoff > 0 ? this.backoff : this.opts.intervalMs;
141
+ this.timer = setTimeout(() => void this.tick(), nextDelay);
142
+ // Allow Node to exit even when the timer is pending.
143
+ if (typeof this.timer.unref === 'function') {
144
+ this.timer.unref?.();
145
+ }
146
+ }
147
+ }
148
+ //# sourceMappingURL=status-reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-reporter.js","sourceRoot":"","sources":["../../src/status-reporter.ts"],"names":[],"mappings":"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"}
@@ -17,6 +17,14 @@ interface StreamOptions {
17
17
  onChange: FlagChangeHandler;
18
18
  /** Called when the stream encounters an error. */
19
19
  onError?: StreamErrorHandler;
20
+ /** Called once the SSE connection is established (HTTP 200). */
21
+ onOpen?: () => void;
22
+ /**
23
+ * Called on any inbound stream activity — heartbeat comments (`:ping`) and
24
+ * data events alike. Lets the sync engine treat the stream as alive and gate
25
+ * its resync cadence on stream liveness.
26
+ */
27
+ onHeartbeat?: () => void;
20
28
  }
21
29
  /**
22
30
  * Lightweight SSE streaming client that keeps the local flag cache
@@ -34,6 +42,8 @@ export declare class FlagStreamClient {
34
42
  private readonly headers;
35
43
  private readonly onChange;
36
44
  private readonly onError;
45
+ private readonly onOpen;
46
+ private readonly onHeartbeat;
37
47
  constructor(options: StreamOptions);
38
48
  /** Open the SSE connection and start processing events. */
39
49
  connect(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"streaming.d.ts","sourceRoot":"","sources":["../../src/streaming.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,0EAA0E;AAC1E,MAAM,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;AAElE,oEAAoE;AACpE,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;AAOxD,UAAU,aAAa;IACrB,+DAA+D;IAC/D,GAAG,EAAE,MAAM,CAAC;IACZ,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,8DAA8D;IAC9D,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,kDAAkD;IAClD,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAwB;IAEvC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;gBAEjC,OAAO,EAAE,aAAa;IAOlC,2DAA2D;IACrD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAuC9B,2DAA2D;IAC3D,KAAK,IAAI,IAAI;YAkBC,aAAa;IA8B3B,OAAO,CAAC,YAAY;IA+BpB,OAAO,CAAC,iBAAiB;CAgB1B"}
1
+ {"version":3,"file":"streaming.d.ts","sourceRoot":"","sources":["../../src/streaming.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,0EAA0E;AAC1E,MAAM,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;AAElE,oEAAoE;AACpE,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;AAOxD,UAAU,aAAa;IACrB,+DAA+D;IAC/D,GAAG,EAAE,MAAM,CAAC;IACZ,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,8DAA8D;IAC9D,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,kDAAkD;IAClD,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAwB;IAEvC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAa;gBAE7B,OAAO,EAAE,aAAa;IASlC,2DAA2D;IACrD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC9B,2DAA2D;IAC3D,KAAK,IAAI,IAAI;YAkBC,aAAa;IA8B3B,OAAO,CAAC,YAAY;IAmCpB,OAAO,CAAC,iBAAiB;CAgB1B"}
@@ -18,11 +18,15 @@ export class FlagStreamClient {
18
18
  headers;
19
19
  onChange;
20
20
  onError;
21
+ onOpen;
22
+ onHeartbeat;
21
23
  constructor(options) {
22
24
  this.url = options.url;
23
25
  this.headers = options.headers;
24
26
  this.onChange = options.onChange;
25
27
  this.onError = options.onError ?? (() => { });
28
+ this.onOpen = options.onOpen ?? (() => { });
29
+ this.onHeartbeat = options.onHeartbeat ?? (() => { });
26
30
  }
27
31
  /** Open the SSE connection and start processing events. */
28
32
  async connect() {
@@ -46,6 +50,7 @@ export class FlagStreamClient {
46
50
  throw new Error('SSE response has no body');
47
51
  }
48
52
  this.retryMs = SSE_INITIAL_RETRY_MS;
53
+ this.onOpen();
49
54
  await this.consumeStream(response.body);
50
55
  }
51
56
  catch (err) {
@@ -100,6 +105,9 @@ export class FlagStreamClient {
100
105
  }
101
106
  }
102
107
  processEvent(raw) {
108
+ // Any inbound frame — including a heartbeat comment — means the stream is
109
+ // alive. Surface that before interpreting the payload.
110
+ this.onHeartbeat();
103
111
  let eventType = 'message';
104
112
  let data = '';
105
113
  for (const line of raw.split('\n')) {
@@ -110,7 +118,7 @@ export class FlagStreamClient {
110
118
  data += line.slice(5).trim();
111
119
  }
112
120
  else if (line.startsWith(':')) {
113
- // SSE comment (heartbeat) — ignore.
121
+ // SSE comment (heartbeat) — liveness already recorded above; no payload.
114
122
  return;
115
123
  }
116
124
  }
@@ -1 +1 @@
1
- {"version":3,"file":"streaming.js","sourceRoot":"","sources":["../../src/streaming.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"streaming.js","sourceRoot":"","sources":["../../src/streaming.ts"],"names":[],"mappings":"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"}