@smplkit/sdk 3.0.7 → 3.0.8

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/dist/index.d.cts CHANGED
@@ -1,5 +1,101 @@
1
1
  import createClient from 'openapi-fetch';
2
2
 
3
+ /**
4
+ * Audit event resource types — surface exposed by `client.audit.events.*`.
5
+ *
6
+ * ADR-047 §2.3.1.
7
+ */
8
+ interface AuditEvent {
9
+ id: string;
10
+ action: string;
11
+ resourceType: string;
12
+ resourceId: string;
13
+ occurredAt: string;
14
+ createdAt: string;
15
+ actorType: string;
16
+ actorId: string | null;
17
+ actorLabel: string;
18
+ snapshot: Record<string, unknown> | null;
19
+ data: Record<string, unknown>;
20
+ idempotencyKey: string;
21
+ }
22
+ interface CreateEventInput {
23
+ action: string;
24
+ resourceType: string;
25
+ resourceId: string;
26
+ /** Defaults to server-side now() if omitted. */
27
+ occurredAt?: Date | string;
28
+ snapshot?: Record<string, unknown>;
29
+ data?: Record<string, unknown>;
30
+ /** Optional caller-supplied idempotency key. Server derives one from event content if absent. */
31
+ idempotencyKey?: string;
32
+ }
33
+ interface ListEventsParams {
34
+ action?: string;
35
+ resourceType?: string;
36
+ resourceId?: string;
37
+ actorType?: string;
38
+ actorId?: string;
39
+ /** Range notation per ADR-014, e.g. `[2026-01-01T00:00:00Z,*)`. */
40
+ occurredAtRange?: string;
41
+ pageSize?: number;
42
+ pageAfter?: string;
43
+ }
44
+ interface ListEventsPage {
45
+ events: AuditEvent[];
46
+ /** Opaque cursor for the next page, or null if this is the last page. */
47
+ nextCursor: string | null;
48
+ }
49
+
50
+ /**
51
+ * Audit namespace — `client.audit.events.{create,list,get}`.
52
+ *
53
+ * ADR-047 §2.6. Writes are fire-and-forget by default: `create` enqueues the
54
+ * event onto an in-memory bounded buffer and returns immediately. The buffer
55
+ * worker flushes on a timer or when depth crosses a watermark, and retries
56
+ * transient failures with exponential backoff. Reads are async and return
57
+ * Promises that resolve to typed `AuditEvent` instances.
58
+ */
59
+
60
+ declare class EventsClient {
61
+ private readonly _apiKey;
62
+ private readonly _baseUrl;
63
+ private readonly _timeoutMs;
64
+ private readonly _buffer;
65
+ /** @internal */
66
+ _fetch: typeof fetch;
67
+ constructor(opts: {
68
+ apiKey: string;
69
+ baseUrl: string;
70
+ timeoutMs?: number;
71
+ });
72
+ /**
73
+ * Enqueue an audit event for asynchronous delivery.
74
+ * Returns immediately. The actual POST happens on the buffer worker.
75
+ *
76
+ * Customers may not emit `smpl.*` resource types — the server will
77
+ * reject those with a 403 (the buffer logs and drops permanent
78
+ * failures, so a misuse will silently disappear from the queue).
79
+ */
80
+ create(input: CreateEventInput): void;
81
+ list(params?: ListEventsParams): Promise<ListEventsPage>;
82
+ get(eventId: string): Promise<AuditEvent>;
83
+ /** Block until the in-memory buffer is drained or `timeoutMs` elapses. */
84
+ flush(timeoutMs?: number): Promise<void>;
85
+ /** @internal */
86
+ _close(): Promise<void>;
87
+ }
88
+ declare class AuditClient {
89
+ readonly events: EventsClient;
90
+ constructor(opts: {
91
+ apiKey: string;
92
+ baseUrl: string;
93
+ timeoutMs?: number;
94
+ });
95
+ /** @internal */
96
+ _close(): Promise<void>;
97
+ }
98
+
3
99
  /**
4
100
  * Internal SDK telemetry engine.
5
101
  *
@@ -2748,6 +2844,8 @@ declare class SmplClient {
2748
2844
  readonly flags: FlagsClient;
2749
2845
  /** Client for logging management and runtime. */
2750
2846
  readonly logging: LoggingClient;
2847
+ /** Client for the audit service — fire-and-forget event recording (ADR-047). */
2848
+ readonly audit: AuditClient;
2751
2849
  /**
2752
2850
  * Standalone management/CRUD entry point — mirrors Python's
2753
2851
  * `client.manage`. Construction is side-effect-free; safe to use even
@@ -2901,4 +2999,4 @@ declare class SmplValidationError extends SmplError {
2901
2999
  /** @deprecated Use {@link ApiErrorDetail}. */
2902
3000
  type ApiErrorObject = ApiErrorDetail;
2903
3001
 
2904
- export { AccountSettings, AccountSettingsClient, type ApiErrorDetail, type ApiErrorObject, BooleanFlag, Color, Config, ConfigChangeEvent, ConfigClient, ConfigEnvironment, ConfigItem, Context, ContextType, ContextTypesClient, ContextsClient, Environment, EnvironmentClassification, EnvironmentsClient, Flag, FlagChangeEvent, FlagDeclaration, FlagEnvironment, FlagRule, FlagStats, FlagValue, FlagsClient, ItemType, JsonFlag, LiveConfigProxy, LogGroup, LogLevel, Logger, LoggerChangeEvent, LoggerEnvironment, LoggerSource, type LoggingAdapter, LoggingClient, NumberFlag, Op, PinoAdapter, type PinoAdapterConfig, Rule, SharedWebSocket, SmplClient, type SmplClientOptions, SmplConflictError, SmplConnectionError, SmplError, SmplManagementClient, type SmplManagementClientOptions, SmplNotFoundError, SmplTimeoutError, SmplValidationError, SmplConflictError as SmplkitConflictError, SmplConnectionError as SmplkitConnectionError, SmplError as SmplkitError, SmplNotFoundError as SmplkitNotFoundError, SmplTimeoutError as SmplkitTimeoutError, SmplValidationError as SmplkitValidationError, StringFlag, WinstonAdapter, type WinstonAdapterConfig };
3002
+ export { AccountSettings, AccountSettingsClient, type ApiErrorDetail, type ApiErrorObject, AuditClient, type AuditEvent, type ListEventsPage as AuditEventListPage, type ListEventsParams as AuditEventListParams, BooleanFlag, Color, Config, ConfigChangeEvent, ConfigClient, ConfigEnvironment, ConfigItem, Context, ContextType, ContextTypesClient, ContextsClient, type CreateEventInput as CreateAuditEventInput, Environment, EnvironmentClassification, EnvironmentsClient, Flag, FlagChangeEvent, FlagDeclaration, FlagEnvironment, FlagRule, FlagStats, FlagValue, FlagsClient, ItemType, JsonFlag, LiveConfigProxy, LogGroup, LogLevel, Logger, LoggerChangeEvent, LoggerEnvironment, LoggerSource, type LoggingAdapter, LoggingClient, NumberFlag, Op, PinoAdapter, type PinoAdapterConfig, Rule, SharedWebSocket, SmplClient, type SmplClientOptions, SmplConflictError, SmplConnectionError, SmplError, SmplManagementClient, type SmplManagementClientOptions, SmplNotFoundError, SmplTimeoutError, SmplValidationError, SmplConflictError as SmplkitConflictError, SmplConnectionError as SmplkitConnectionError, SmplError as SmplkitError, SmplNotFoundError as SmplkitNotFoundError, SmplTimeoutError as SmplkitTimeoutError, SmplValidationError as SmplkitValidationError, StringFlag, WinstonAdapter, type WinstonAdapterConfig };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,101 @@
1
1
  import createClient from 'openapi-fetch';
2
2
 
3
+ /**
4
+ * Audit event resource types — surface exposed by `client.audit.events.*`.
5
+ *
6
+ * ADR-047 §2.3.1.
7
+ */
8
+ interface AuditEvent {
9
+ id: string;
10
+ action: string;
11
+ resourceType: string;
12
+ resourceId: string;
13
+ occurredAt: string;
14
+ createdAt: string;
15
+ actorType: string;
16
+ actorId: string | null;
17
+ actorLabel: string;
18
+ snapshot: Record<string, unknown> | null;
19
+ data: Record<string, unknown>;
20
+ idempotencyKey: string;
21
+ }
22
+ interface CreateEventInput {
23
+ action: string;
24
+ resourceType: string;
25
+ resourceId: string;
26
+ /** Defaults to server-side now() if omitted. */
27
+ occurredAt?: Date | string;
28
+ snapshot?: Record<string, unknown>;
29
+ data?: Record<string, unknown>;
30
+ /** Optional caller-supplied idempotency key. Server derives one from event content if absent. */
31
+ idempotencyKey?: string;
32
+ }
33
+ interface ListEventsParams {
34
+ action?: string;
35
+ resourceType?: string;
36
+ resourceId?: string;
37
+ actorType?: string;
38
+ actorId?: string;
39
+ /** Range notation per ADR-014, e.g. `[2026-01-01T00:00:00Z,*)`. */
40
+ occurredAtRange?: string;
41
+ pageSize?: number;
42
+ pageAfter?: string;
43
+ }
44
+ interface ListEventsPage {
45
+ events: AuditEvent[];
46
+ /** Opaque cursor for the next page, or null if this is the last page. */
47
+ nextCursor: string | null;
48
+ }
49
+
50
+ /**
51
+ * Audit namespace — `client.audit.events.{create,list,get}`.
52
+ *
53
+ * ADR-047 §2.6. Writes are fire-and-forget by default: `create` enqueues the
54
+ * event onto an in-memory bounded buffer and returns immediately. The buffer
55
+ * worker flushes on a timer or when depth crosses a watermark, and retries
56
+ * transient failures with exponential backoff. Reads are async and return
57
+ * Promises that resolve to typed `AuditEvent` instances.
58
+ */
59
+
60
+ declare class EventsClient {
61
+ private readonly _apiKey;
62
+ private readonly _baseUrl;
63
+ private readonly _timeoutMs;
64
+ private readonly _buffer;
65
+ /** @internal */
66
+ _fetch: typeof fetch;
67
+ constructor(opts: {
68
+ apiKey: string;
69
+ baseUrl: string;
70
+ timeoutMs?: number;
71
+ });
72
+ /**
73
+ * Enqueue an audit event for asynchronous delivery.
74
+ * Returns immediately. The actual POST happens on the buffer worker.
75
+ *
76
+ * Customers may not emit `smpl.*` resource types — the server will
77
+ * reject those with a 403 (the buffer logs and drops permanent
78
+ * failures, so a misuse will silently disappear from the queue).
79
+ */
80
+ create(input: CreateEventInput): void;
81
+ list(params?: ListEventsParams): Promise<ListEventsPage>;
82
+ get(eventId: string): Promise<AuditEvent>;
83
+ /** Block until the in-memory buffer is drained or `timeoutMs` elapses. */
84
+ flush(timeoutMs?: number): Promise<void>;
85
+ /** @internal */
86
+ _close(): Promise<void>;
87
+ }
88
+ declare class AuditClient {
89
+ readonly events: EventsClient;
90
+ constructor(opts: {
91
+ apiKey: string;
92
+ baseUrl: string;
93
+ timeoutMs?: number;
94
+ });
95
+ /** @internal */
96
+ _close(): Promise<void>;
97
+ }
98
+
3
99
  /**
4
100
  * Internal SDK telemetry engine.
5
101
  *
@@ -2748,6 +2844,8 @@ declare class SmplClient {
2748
2844
  readonly flags: FlagsClient;
2749
2845
  /** Client for logging management and runtime. */
2750
2846
  readonly logging: LoggingClient;
2847
+ /** Client for the audit service — fire-and-forget event recording (ADR-047). */
2848
+ readonly audit: AuditClient;
2751
2849
  /**
2752
2850
  * Standalone management/CRUD entry point — mirrors Python's
2753
2851
  * `client.manage`. Construction is side-effect-free; safe to use even
@@ -2901,4 +2999,4 @@ declare class SmplValidationError extends SmplError {
2901
2999
  /** @deprecated Use {@link ApiErrorDetail}. */
2902
3000
  type ApiErrorObject = ApiErrorDetail;
2903
3001
 
2904
- export { AccountSettings, AccountSettingsClient, type ApiErrorDetail, type ApiErrorObject, BooleanFlag, Color, Config, ConfigChangeEvent, ConfigClient, ConfigEnvironment, ConfigItem, Context, ContextType, ContextTypesClient, ContextsClient, Environment, EnvironmentClassification, EnvironmentsClient, Flag, FlagChangeEvent, FlagDeclaration, FlagEnvironment, FlagRule, FlagStats, FlagValue, FlagsClient, ItemType, JsonFlag, LiveConfigProxy, LogGroup, LogLevel, Logger, LoggerChangeEvent, LoggerEnvironment, LoggerSource, type LoggingAdapter, LoggingClient, NumberFlag, Op, PinoAdapter, type PinoAdapterConfig, Rule, SharedWebSocket, SmplClient, type SmplClientOptions, SmplConflictError, SmplConnectionError, SmplError, SmplManagementClient, type SmplManagementClientOptions, SmplNotFoundError, SmplTimeoutError, SmplValidationError, SmplConflictError as SmplkitConflictError, SmplConnectionError as SmplkitConnectionError, SmplError as SmplkitError, SmplNotFoundError as SmplkitNotFoundError, SmplTimeoutError as SmplkitTimeoutError, SmplValidationError as SmplkitValidationError, StringFlag, WinstonAdapter, type WinstonAdapterConfig };
3002
+ export { AccountSettings, AccountSettingsClient, type ApiErrorDetail, type ApiErrorObject, AuditClient, type AuditEvent, type ListEventsPage as AuditEventListPage, type ListEventsParams as AuditEventListParams, BooleanFlag, Color, Config, ConfigChangeEvent, ConfigClient, ConfigEnvironment, ConfigItem, Context, ContextType, ContextTypesClient, ContextsClient, type CreateEventInput as CreateAuditEventInput, Environment, EnvironmentClassification, EnvironmentsClient, Flag, FlagChangeEvent, FlagDeclaration, FlagEnvironment, FlagRule, FlagStats, FlagValue, FlagsClient, ItemType, JsonFlag, LiveConfigProxy, LogGroup, LogLevel, Logger, LoggerChangeEvent, LoggerEnvironment, LoggerSource, type LoggingAdapter, LoggingClient, NumberFlag, Op, PinoAdapter, type PinoAdapterConfig, Rule, SharedWebSocket, SmplClient, type SmplClientOptions, SmplConflictError, SmplConnectionError, SmplError, SmplManagementClient, type SmplManagementClientOptions, SmplNotFoundError, SmplTimeoutError, SmplValidationError, SmplConflictError as SmplkitConflictError, SmplConnectionError as SmplkitConnectionError, SmplError as SmplkitError, SmplNotFoundError as SmplkitNotFoundError, SmplTimeoutError as SmplkitTimeoutError, SmplValidationError as SmplkitValidationError, StringFlag, WinstonAdapter, type WinstonAdapterConfig };
package/dist/index.js CHANGED
@@ -16616,6 +16616,264 @@ var require_pino = __commonJS({
16616
16616
  // src/client.ts
16617
16617
  import createClient5 from "openapi-fetch";
16618
16618
 
16619
+ // src/audit/buffer.ts
16620
+ var MAX_BUFFER_SIZE = 1e3;
16621
+ var PERIODIC_FLUSH_INTERVAL_MS = 5e3;
16622
+ var HIGH_WATERMARK = 50;
16623
+ var MAX_ATTEMPTS_PER_ITEM = 5;
16624
+ var INITIAL_BACKOFF_MS = 250;
16625
+ var MAX_BACKOFF_MS = 8e3;
16626
+ var AuditEventBuffer = class {
16627
+ _queue = [];
16628
+ _post;
16629
+ _maxSize;
16630
+ _watermark;
16631
+ _flushTimer;
16632
+ _draining = false;
16633
+ _closed = false;
16634
+ _droppedCount = 0;
16635
+ constructor(opts) {
16636
+ this._post = opts.post;
16637
+ this._maxSize = opts.maxSize ?? MAX_BUFFER_SIZE;
16638
+ this._watermark = opts.watermark ?? HIGH_WATERMARK;
16639
+ const interval = opts.flushIntervalMs ?? PERIODIC_FLUSH_INTERVAL_MS;
16640
+ this._flushTimer = setInterval(() => {
16641
+ void this._drainOnce();
16642
+ }, interval);
16643
+ if (typeof this._flushTimer.unref === "function") {
16644
+ this._flushTimer.unref();
16645
+ }
16646
+ }
16647
+ /** Enqueue a new event. May evict the oldest queued item if full. */
16648
+ enqueue(body, idempotencyKey = null) {
16649
+ if (this._closed) return;
16650
+ if (this._queue.length >= this._maxSize) {
16651
+ this._queue.shift();
16652
+ this._droppedCount += 1;
16653
+ console.warn(
16654
+ `[smplkit.audit] buffer full (size=${this._maxSize}); dropped oldest event (total dropped=${this._droppedCount})`
16655
+ );
16656
+ }
16657
+ this._queue.push({ body, idempotencyKey, attempts: 0, nextRetryAt: 0 });
16658
+ if (this._queue.length >= this._watermark) {
16659
+ void this._drainOnce();
16660
+ }
16661
+ }
16662
+ /** Block (cooperatively) until the buffer is empty or `timeoutMs` elapses. */
16663
+ async flush(timeoutMs = 5e3) {
16664
+ const deadline = Date.now() + timeoutMs;
16665
+ while (this._queue.length > 0) {
16666
+ if (Date.now() >= deadline) {
16667
+ console.warn(`[smplkit.audit] flush timed out after ${timeoutMs}ms`);
16668
+ return;
16669
+ }
16670
+ void this._drainOnce();
16671
+ await new Promise((r) => setTimeout(r, 50));
16672
+ }
16673
+ }
16674
+ /** Stop the periodic timer, drain best-effort, and mark closed. */
16675
+ async close(timeoutMs = 5e3) {
16676
+ this._closed = true;
16677
+ await this.flush(timeoutMs);
16678
+ if (this._flushTimer !== null) {
16679
+ clearInterval(this._flushTimer);
16680
+ this._flushTimer = null;
16681
+ }
16682
+ }
16683
+ async _drainOnce() {
16684
+ if (this._draining) return;
16685
+ this._draining = true;
16686
+ try {
16687
+ const now = Date.now();
16688
+ while (this._queue.length > 0) {
16689
+ const head = this._queue[0];
16690
+ if (head.nextRetryAt > now) break;
16691
+ this._queue.shift();
16692
+ let outcome;
16693
+ try {
16694
+ outcome = await this._post(head);
16695
+ } catch (err) {
16696
+ outcome = { status: 0 };
16697
+ }
16698
+ const requeued = this._handleOutcome(head, outcome);
16699
+ if (requeued !== null) {
16700
+ this._queue.unshift(requeued);
16701
+ break;
16702
+ }
16703
+ }
16704
+ } finally {
16705
+ this._draining = false;
16706
+ }
16707
+ }
16708
+ _handleOutcome(item, outcome) {
16709
+ if (outcome.status >= 200 && outcome.status < 300) return null;
16710
+ if (outcome.status >= 400 && outcome.status < 500 && outcome.status !== 429) {
16711
+ console.warn(`[smplkit.audit] permanent failure status=${outcome.status}; event dropped`);
16712
+ return null;
16713
+ }
16714
+ item.attempts += 1;
16715
+ if (item.attempts >= MAX_ATTEMPTS_PER_ITEM) {
16716
+ console.warn(
16717
+ `[smplkit.audit] gave up after ${item.attempts} attempts (last_status=${outcome.status})`
16718
+ );
16719
+ return null;
16720
+ }
16721
+ const backoff = Math.min(MAX_BACKOFF_MS, INITIAL_BACKOFF_MS * 2 ** (item.attempts - 1));
16722
+ const jitter = Math.random() * backoff * 0.25;
16723
+ item.nextRetryAt = Date.now() + backoff + jitter;
16724
+ return item;
16725
+ }
16726
+ };
16727
+
16728
+ // src/audit/client.ts
16729
+ var JSONAPI_HEADERS = {
16730
+ "Content-Type": "application/vnd.api+json",
16731
+ Accept: "application/vnd.api+json"
16732
+ };
16733
+ function _attributesFromInput(input) {
16734
+ const attrs = {
16735
+ action: input.action,
16736
+ resource_type: input.resourceType,
16737
+ resource_id: input.resourceId
16738
+ };
16739
+ if (input.occurredAt !== void 0) {
16740
+ const ts = input.occurredAt instanceof Date ? input.occurredAt.toISOString() : input.occurredAt;
16741
+ attrs.occurred_at = ts;
16742
+ }
16743
+ if (input.snapshot !== void 0) attrs.snapshot = input.snapshot;
16744
+ if (input.data !== void 0) attrs.data = input.data;
16745
+ return attrs;
16746
+ }
16747
+ function _eventFromResource(resource) {
16748
+ const attrs = resource.attributes;
16749
+ return {
16750
+ id: resource.id,
16751
+ action: String(attrs.action ?? ""),
16752
+ resourceType: String(attrs.resource_type ?? ""),
16753
+ resourceId: String(attrs.resource_id ?? ""),
16754
+ occurredAt: String(attrs.occurred_at ?? ""),
16755
+ createdAt: String(attrs.created_at ?? ""),
16756
+ actorType: String(attrs.actor_type ?? ""),
16757
+ actorId: attrs.actor_id ?? null,
16758
+ actorLabel: String(attrs.actor_label ?? ""),
16759
+ snapshot: attrs.snapshot ?? null,
16760
+ data: attrs.data ?? {},
16761
+ idempotencyKey: String(attrs.idempotency_key ?? "")
16762
+ };
16763
+ }
16764
+ var EventsClient = class {
16765
+ _apiKey;
16766
+ _baseUrl;
16767
+ _timeoutMs;
16768
+ _buffer;
16769
+ /** @internal */
16770
+ _fetch = fetch;
16771
+ constructor(opts) {
16772
+ this._apiKey = opts.apiKey;
16773
+ this._baseUrl = opts.baseUrl.replace(/\/$/, "");
16774
+ this._timeoutMs = opts.timeoutMs ?? 1e4;
16775
+ this._buffer = new AuditEventBuffer({
16776
+ post: async (item) => {
16777
+ try {
16778
+ const headers = {
16779
+ ...JSONAPI_HEADERS,
16780
+ Authorization: `Bearer ${this._apiKey}`
16781
+ };
16782
+ if (item.idempotencyKey !== null) headers["Idempotency-Key"] = item.idempotencyKey;
16783
+ const ctrl = new AbortController();
16784
+ const t = setTimeout(() => ctrl.abort(), this._timeoutMs);
16785
+ try {
16786
+ const resp = await this._fetch(`${this._baseUrl}/api/v1/events`, {
16787
+ method: "POST",
16788
+ headers,
16789
+ body: JSON.stringify(item.body),
16790
+ signal: ctrl.signal
16791
+ });
16792
+ return { status: resp.status };
16793
+ } finally {
16794
+ clearTimeout(t);
16795
+ }
16796
+ } catch {
16797
+ return { status: 0 };
16798
+ }
16799
+ }
16800
+ });
16801
+ }
16802
+ /**
16803
+ * Enqueue an audit event for asynchronous delivery.
16804
+ * Returns immediately. The actual POST happens on the buffer worker.
16805
+ *
16806
+ * Customers may not emit `smpl.*` resource types — the server will
16807
+ * reject those with a 403 (the buffer logs and drops permanent
16808
+ * failures, so a misuse will silently disappear from the queue).
16809
+ */
16810
+ create(input) {
16811
+ const body = {
16812
+ data: { type: "event", attributes: _attributesFromInput(input) }
16813
+ };
16814
+ this._buffer.enqueue(body, input.idempotencyKey ?? null);
16815
+ }
16816
+ async list(params = {}) {
16817
+ const qs = new URLSearchParams();
16818
+ if (params.action !== void 0) qs.set("filter[action]", params.action);
16819
+ if (params.resourceType !== void 0) qs.set("filter[resource_type]", params.resourceType);
16820
+ if (params.resourceId !== void 0) qs.set("filter[resource_id]", params.resourceId);
16821
+ if (params.actorType !== void 0) qs.set("filter[actor_type]", params.actorType);
16822
+ if (params.actorId !== void 0) qs.set("filter[actor_id]", params.actorId);
16823
+ if (params.occurredAtRange !== void 0) qs.set("filter[occurred_at]", params.occurredAtRange);
16824
+ if (params.pageSize !== void 0) qs.set("page[size]", String(params.pageSize));
16825
+ if (params.pageAfter !== void 0) qs.set("page[after]", params.pageAfter);
16826
+ const resp = await this._fetch(`${this._baseUrl}/api/v1/events?${qs.toString()}`, {
16827
+ headers: {
16828
+ Authorization: `Bearer ${this._apiKey}`,
16829
+ Accept: "application/vnd.api+json"
16830
+ }
16831
+ });
16832
+ if (!resp.ok) {
16833
+ throw new Error(`audit list failed: ${resp.status} ${resp.statusText}`);
16834
+ }
16835
+ const body = await resp.json();
16836
+ const events = (body.data ?? []).map(_eventFromResource);
16837
+ let nextCursor = null;
16838
+ const nextLink = body.links?.next;
16839
+ if (typeof nextLink === "string" && nextLink.includes("page[after]=")) {
16840
+ nextCursor = nextLink.split("page[after]=")[1];
16841
+ }
16842
+ return { events, nextCursor };
16843
+ }
16844
+ async get(eventId) {
16845
+ const resp = await this._fetch(`${this._baseUrl}/api/v1/events/${eventId}`, {
16846
+ headers: {
16847
+ Authorization: `Bearer ${this._apiKey}`,
16848
+ Accept: "application/vnd.api+json"
16849
+ }
16850
+ });
16851
+ if (!resp.ok) {
16852
+ throw new Error(`audit get failed: ${resp.status} ${resp.statusText}`);
16853
+ }
16854
+ const body = await resp.json();
16855
+ return _eventFromResource(body.data);
16856
+ }
16857
+ /** Block until the in-memory buffer is drained or `timeoutMs` elapses. */
16858
+ async flush(timeoutMs = 5e3) {
16859
+ await this._buffer.flush(timeoutMs);
16860
+ }
16861
+ /** @internal */
16862
+ async _close() {
16863
+ await this._buffer.close();
16864
+ }
16865
+ };
16866
+ var AuditClient = class {
16867
+ events;
16868
+ constructor(opts) {
16869
+ this.events = new EventsClient(opts);
16870
+ }
16871
+ /** @internal */
16872
+ async _close() {
16873
+ await this.events._close();
16874
+ }
16875
+ };
16876
+
16619
16877
  // src/config/client.ts
16620
16878
  import createClient from "openapi-fetch";
16621
16879
 
@@ -21892,6 +22150,8 @@ var SmplClient = class {
21892
22150
  flags;
21893
22151
  /** Client for logging management and runtime. */
21894
22152
  logging;
22153
+ /** Client for the audit service — fire-and-forget event recording (ADR-047). */
22154
+ audit;
21895
22155
  /**
21896
22156
  * Standalone management/CRUD entry point — mirrors Python's
21897
22157
  * `client.manage`. Construction is side-effect-free; safe to use even
@@ -21922,6 +22182,7 @@ var SmplClient = class {
21922
22182
  const configBaseUrl = serviceUrl(cfg.scheme, "config", cfg.baseDomain);
21923
22183
  const flagsBaseUrl = serviceUrl(cfg.scheme, "flags", cfg.baseDomain);
21924
22184
  const loggingBaseUrl = serviceUrl(cfg.scheme, "logging", cfg.baseDomain);
22185
+ const auditBaseUrl = serviceUrl(cfg.scheme, "audit", cfg.baseDomain);
21925
22186
  this._appBaseUrl = appBaseUrl;
21926
22187
  const maskedKey = cfg.apiKey.length > 14 ? cfg.apiKey.slice(0, 10) + "..." + cfg.apiKey.slice(-4) : cfg.apiKey.slice(0, Math.min(4, cfg.apiKey.length)) + "...";
21927
22188
  debug(
@@ -21964,6 +22225,11 @@ var SmplClient = class {
21964
22225
  this._timeout,
21965
22226
  loggingBaseUrl
21966
22227
  );
22228
+ this.audit = new AuditClient({
22229
+ apiKey: cfg.apiKey,
22230
+ baseUrl: auditBaseUrl,
22231
+ timeoutMs: this._timeout
22232
+ });
21967
22233
  this.config._getSharedWs = () => this._ensureWs();
21968
22234
  this.flags._parent = this;
21969
22235
  this.config._parent = this;
@@ -22043,6 +22309,7 @@ var SmplClient = class {
22043
22309
  }
22044
22310
  this.flags._close();
22045
22311
  this.logging._close();
22312
+ void this.audit._close();
22046
22313
  if (this._wsManager !== null) {
22047
22314
  this._wsManager.stop();
22048
22315
  this._wsManager = null;
@@ -22276,6 +22543,7 @@ var PinoAdapter = class {
22276
22543
  export {
22277
22544
  AccountSettings,
22278
22545
  AccountSettingsClient,
22546
+ AuditClient,
22279
22547
  BooleanFlag,
22280
22548
  Color,
22281
22549
  Config,