sdk-garden-js 0.2.0 → 0.3.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.
package/README.md CHANGED
@@ -63,6 +63,8 @@ Requires Node.js 18+ (for the global `fetch`) or any modern browser.
63
63
  | `startedAt` | `number` | module load time | `Date.now()` when your SDK began initializing; reported as `time_to_init_ms` for the dashboard's init timing |
64
64
  | `methods` | `string[]` | `[]` | Public methods of your SDK that are not wrapped with `trackMethod()`, so the dashboard's API Surface panel can show them when nobody calls them |
65
65
  | `customer` | `{ id: string; name?: string }` | _(none)_ | The app or customer account this session runs for (see [Customers](#customers)) |
66
+ | `advisories` | `boolean` | `true` | Show the version advisories you set on the Releases page as one `console.warn` per process (0.3.0+) |
67
+ | `onAdvisory` | `(advisory) => void` | _(none)_ | Handle an advisory yourself instead: `{ version, severity, message, upgradeTo? }` |
66
68
 
67
69
  ### API surface
68
70
 
package/dist/index.d.mts CHANGED
@@ -60,6 +60,29 @@ interface SdkGardenConfig {
60
60
  * setCustomer(). Default: none.
61
61
  */
62
62
  customer?: SdkGardenCustomer;
63
+ /**
64
+ * Show the notices you attach to versions on the SDK Garden Releases page
65
+ * (deprecated, broken, security) to developers still running them, as one
66
+ * console.warn per process. Default: true. Set onAdvisory to handle them
67
+ * yourself instead.
68
+ */
69
+ advisories?: boolean;
70
+ /**
71
+ * Called once per process when the version in use has an advisory, instead
72
+ * of the default console.warn. Use it to route the notice through your
73
+ * SDK's own logger. Exceptions it throws are ignored.
74
+ */
75
+ onAdvisory?: (advisory: SdkGardenAdvisory) => void;
76
+ }
77
+ /** A notice the SDK publisher attached to the running version (see SdkGardenConfig.advisories). */
78
+ interface SdkGardenAdvisory {
79
+ /** The version this process runs, e.g. "1.2.0". */
80
+ version: string;
81
+ severity: 'deprecated' | 'broken' | 'security';
82
+ /** The publisher's message; may be empty. */
83
+ message: string;
84
+ /** The version the publisher recommends, if they named one. */
85
+ upgradeTo?: string;
63
86
  }
64
87
  /** The app or customer account a session is attributed to (see setCustomer()). */
65
88
  interface SdkGardenCustomer {
@@ -140,4 +163,4 @@ interface SdkGardenObject {
140
163
  */
141
164
  declare const SdkGarden: SdkGardenObject;
142
165
 
143
- export { type SdkGardenConfig, type SdkGardenCustomer, type SdkMethodDecorator, SdkGarden as default };
166
+ export { type SdkGardenAdvisory, type SdkGardenConfig, type SdkGardenCustomer, type SdkMethodDecorator, SdkGarden as default };
package/dist/index.d.ts CHANGED
@@ -60,6 +60,29 @@ interface SdkGardenConfig {
60
60
  * setCustomer(). Default: none.
61
61
  */
62
62
  customer?: SdkGardenCustomer;
63
+ /**
64
+ * Show the notices you attach to versions on the SDK Garden Releases page
65
+ * (deprecated, broken, security) to developers still running them, as one
66
+ * console.warn per process. Default: true. Set onAdvisory to handle them
67
+ * yourself instead.
68
+ */
69
+ advisories?: boolean;
70
+ /**
71
+ * Called once per process when the version in use has an advisory, instead
72
+ * of the default console.warn. Use it to route the notice through your
73
+ * SDK's own logger. Exceptions it throws are ignored.
74
+ */
75
+ onAdvisory?: (advisory: SdkGardenAdvisory) => void;
76
+ }
77
+ /** A notice the SDK publisher attached to the running version (see SdkGardenConfig.advisories). */
78
+ interface SdkGardenAdvisory {
79
+ /** The version this process runs, e.g. "1.2.0". */
80
+ version: string;
81
+ severity: 'deprecated' | 'broken' | 'security';
82
+ /** The publisher's message; may be empty. */
83
+ message: string;
84
+ /** The version the publisher recommends, if they named one. */
85
+ upgradeTo?: string;
63
86
  }
64
87
  /** The app or customer account a session is attributed to (see setCustomer()). */
65
88
  interface SdkGardenCustomer {
@@ -140,4 +163,4 @@ interface SdkGardenObject {
140
163
  */
141
164
  declare const SdkGarden: SdkGardenObject;
142
165
 
143
- export { type SdkGardenConfig, type SdkGardenCustomer, type SdkMethodDecorator, SdkGarden as default };
166
+ export { type SdkGardenAdvisory, type SdkGardenConfig, type SdkGardenCustomer, type SdkMethodDecorator, SdkGarden as default };
package/dist/index.js CHANGED
@@ -41,6 +41,8 @@ var _pending = /* @__PURE__ */ new Set();
41
41
  var _registered = /* @__PURE__ */ new Set();
42
42
  var _undeclared = [];
43
43
  var _declareScheduled = false;
44
+ var _advisoriesShown = /* @__PURE__ */ new Set();
45
+ var ADVISORY_HEADER = "SDK-Garden-Advisory";
44
46
  var DEFAULT_ENDPOINT = "https://ingest.sdkgarden.com/track";
45
47
  var MAX_BATCH_SIZE = 100;
46
48
  var MAX_QUEUE_SIZE = 1e3;
@@ -69,7 +71,7 @@ function _clamp(value, min, max, fallback) {
69
71
  return Math.min(max, Math.max(min, Math.floor(value)));
70
72
  }
71
73
  function _mergeDefaults(config) {
72
- var _a, _b;
74
+ var _a, _b, _c;
73
75
  return {
74
76
  apiKey: config.apiKey,
75
77
  sdkName: config.sdkName,
@@ -81,7 +83,9 @@ function _mergeDefaults(config) {
81
83
  // A batch size below 1 would never empty the queue; above 100 the endpoint rejects it.
82
84
  batchSize: _clamp(config.batchSize, 1, MAX_BATCH_SIZE, 10),
83
85
  // A tiny interval would send constantly; one too large for a timer would fire every 1 ms.
84
- flushInterval: _clamp(config.flushInterval, 100, MAX_TIMER_MS, 5e3)
86
+ flushInterval: _clamp(config.flushInterval, 100, MAX_TIMER_MS, 5e3),
87
+ advisories: (_c = config.advisories) != null ? _c : true,
88
+ onAdvisory: typeof config.onAdvisory === "function" ? config.onAdvisory : null
85
89
  };
86
90
  }
87
91
  function _generateSessionId() {
@@ -385,6 +389,55 @@ function _takeBatch(events, maxEvents) {
385
389
  }
386
390
  return events.splice(0, count).map((event) => event.json);
387
391
  }
392
+ function _parseAdvisory(raw) {
393
+ var _a;
394
+ const fields = {};
395
+ for (const part of raw.split("&")) {
396
+ const eq = part.indexOf("=");
397
+ if (eq <= 0) continue;
398
+ try {
399
+ fields[decodeURIComponent(part.slice(0, eq))] = decodeURIComponent(part.slice(eq + 1).replace(/\+/g, " "));
400
+ } catch (e) {
401
+ return null;
402
+ }
403
+ }
404
+ const severity = fields["severity"];
405
+ if (!fields["version"] || severity !== "deprecated" && severity !== "broken" && severity !== "security") {
406
+ return null;
407
+ }
408
+ return {
409
+ version: fields["version"],
410
+ severity,
411
+ message: ((_a = fields["message"]) != null ? _a : "").slice(0, 280),
412
+ ...fields["upgrade_to"] ? { upgradeTo: fields["upgrade_to"].slice(0, 50) } : {}
413
+ };
414
+ }
415
+ function _advisoryText(sdkName, a) {
416
+ const what = { deprecated: "is deprecated", broken: "has a known problem", security: "has a security issue" }[a.severity];
417
+ let text = `[${sdkName}] Version ${a.version} ${what}.`;
418
+ if (a.message) text += ` ${a.message}`;
419
+ if (a.upgradeTo) text += ` Upgrade to ${a.upgradeTo}.`;
420
+ return text;
421
+ }
422
+ function _handleAdvisory(config, response) {
423
+ var _a, _b;
424
+ if (!config.advisories) return;
425
+ const raw = (_b = (_a = response == null ? void 0 : response.headers) == null ? void 0 : _a.get) == null ? void 0 : _b.call(_a, ADVISORY_HEADER);
426
+ if (typeof raw !== "string" || raw === "") return;
427
+ const advisory = _parseAdvisory(raw);
428
+ if (!advisory || _advisoriesShown.has(advisory.version)) return;
429
+ _advisoriesShown.add(advisory.version);
430
+ if (config.onAdvisory) {
431
+ try {
432
+ config.onAdvisory(advisory);
433
+ } catch (e) {
434
+ }
435
+ return;
436
+ }
437
+ if (typeof console !== "undefined" && typeof console.warn === "function") {
438
+ console.warn(_advisoryText(config.sdkName, advisory));
439
+ }
440
+ }
388
441
  function _send(config, batch, keepalive) {
389
442
  var _a;
390
443
  if (typeof fetch !== "function") {
@@ -449,6 +502,7 @@ function _send(config, batch, keepalive) {
449
502
  if (stream && typeof stream.cancel === "function") discarded = stream.cancel();
450
503
  } catch (e) {
451
504
  }
505
+ if (delivered) _safely(() => _handleAdvisory(config, response));
452
506
  Promise.resolve(discarded).then(settle, settle);
453
507
  },
454
508
  () => settle()
@@ -846,6 +900,7 @@ var SdkGarden = {
846
900
  _pending.clear();
847
901
  _registered.clear();
848
902
  _undeclared = [];
903
+ _advisoriesShown.clear();
849
904
  }
850
905
  };
851
906
  var index_default = SdkGarden;
package/dist/index.mjs CHANGED
@@ -17,6 +17,8 @@ var _pending = /* @__PURE__ */ new Set();
17
17
  var _registered = /* @__PURE__ */ new Set();
18
18
  var _undeclared = [];
19
19
  var _declareScheduled = false;
20
+ var _advisoriesShown = /* @__PURE__ */ new Set();
21
+ var ADVISORY_HEADER = "SDK-Garden-Advisory";
20
22
  var DEFAULT_ENDPOINT = "https://ingest.sdkgarden.com/track";
21
23
  var MAX_BATCH_SIZE = 100;
22
24
  var MAX_QUEUE_SIZE = 1e3;
@@ -45,7 +47,7 @@ function _clamp(value, min, max, fallback) {
45
47
  return Math.min(max, Math.max(min, Math.floor(value)));
46
48
  }
47
49
  function _mergeDefaults(config) {
48
- var _a, _b;
50
+ var _a, _b, _c;
49
51
  return {
50
52
  apiKey: config.apiKey,
51
53
  sdkName: config.sdkName,
@@ -57,7 +59,9 @@ function _mergeDefaults(config) {
57
59
  // A batch size below 1 would never empty the queue; above 100 the endpoint rejects it.
58
60
  batchSize: _clamp(config.batchSize, 1, MAX_BATCH_SIZE, 10),
59
61
  // A tiny interval would send constantly; one too large for a timer would fire every 1 ms.
60
- flushInterval: _clamp(config.flushInterval, 100, MAX_TIMER_MS, 5e3)
62
+ flushInterval: _clamp(config.flushInterval, 100, MAX_TIMER_MS, 5e3),
63
+ advisories: (_c = config.advisories) != null ? _c : true,
64
+ onAdvisory: typeof config.onAdvisory === "function" ? config.onAdvisory : null
61
65
  };
62
66
  }
63
67
  function _generateSessionId() {
@@ -361,6 +365,55 @@ function _takeBatch(events, maxEvents) {
361
365
  }
362
366
  return events.splice(0, count).map((event) => event.json);
363
367
  }
368
+ function _parseAdvisory(raw) {
369
+ var _a;
370
+ const fields = {};
371
+ for (const part of raw.split("&")) {
372
+ const eq = part.indexOf("=");
373
+ if (eq <= 0) continue;
374
+ try {
375
+ fields[decodeURIComponent(part.slice(0, eq))] = decodeURIComponent(part.slice(eq + 1).replace(/\+/g, " "));
376
+ } catch (e) {
377
+ return null;
378
+ }
379
+ }
380
+ const severity = fields["severity"];
381
+ if (!fields["version"] || severity !== "deprecated" && severity !== "broken" && severity !== "security") {
382
+ return null;
383
+ }
384
+ return {
385
+ version: fields["version"],
386
+ severity,
387
+ message: ((_a = fields["message"]) != null ? _a : "").slice(0, 280),
388
+ ...fields["upgrade_to"] ? { upgradeTo: fields["upgrade_to"].slice(0, 50) } : {}
389
+ };
390
+ }
391
+ function _advisoryText(sdkName, a) {
392
+ const what = { deprecated: "is deprecated", broken: "has a known problem", security: "has a security issue" }[a.severity];
393
+ let text = `[${sdkName}] Version ${a.version} ${what}.`;
394
+ if (a.message) text += ` ${a.message}`;
395
+ if (a.upgradeTo) text += ` Upgrade to ${a.upgradeTo}.`;
396
+ return text;
397
+ }
398
+ function _handleAdvisory(config, response) {
399
+ var _a, _b;
400
+ if (!config.advisories) return;
401
+ const raw = (_b = (_a = response == null ? void 0 : response.headers) == null ? void 0 : _a.get) == null ? void 0 : _b.call(_a, ADVISORY_HEADER);
402
+ if (typeof raw !== "string" || raw === "") return;
403
+ const advisory = _parseAdvisory(raw);
404
+ if (!advisory || _advisoriesShown.has(advisory.version)) return;
405
+ _advisoriesShown.add(advisory.version);
406
+ if (config.onAdvisory) {
407
+ try {
408
+ config.onAdvisory(advisory);
409
+ } catch (e) {
410
+ }
411
+ return;
412
+ }
413
+ if (typeof console !== "undefined" && typeof console.warn === "function") {
414
+ console.warn(_advisoryText(config.sdkName, advisory));
415
+ }
416
+ }
364
417
  function _send(config, batch, keepalive) {
365
418
  var _a;
366
419
  if (typeof fetch !== "function") {
@@ -425,6 +478,7 @@ function _send(config, batch, keepalive) {
425
478
  if (stream && typeof stream.cancel === "function") discarded = stream.cancel();
426
479
  } catch (e) {
427
480
  }
481
+ if (delivered) _safely(() => _handleAdvisory(config, response));
428
482
  Promise.resolve(discarded).then(settle, settle);
429
483
  },
430
484
  () => settle()
@@ -822,6 +876,7 @@ var SdkGarden = {
822
876
  _pending.clear();
823
877
  _registered.clear();
824
878
  _undeclared = [];
879
+ _advisoriesShown.clear();
825
880
  }
826
881
  };
827
882
  var index_default = SdkGarden;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sdk-garden-js",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Lightweight SDK observability client for JavaScript/TypeScript \u2014 zero runtime dependencies",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",