@lessonkit/core 0.9.0 → 0.9.2

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.cjs CHANGED
@@ -219,14 +219,14 @@ function createTrackingClient(opts) {
219
219
  const buffer = [];
220
220
  let flushInFlight = null;
221
221
  let disposed = false;
222
+ let disposing = false;
222
223
  let intervalId;
223
- const flush = () => {
224
- if (disposed || flushInFlight) return;
225
- if (!buffer.length) return;
224
+ const runFlush = () => {
225
+ if (!buffer.length) return Promise.resolve();
226
226
  const events = buffer.splice(0, buffer.length);
227
227
  let sent = 0;
228
228
  let succeeded = false;
229
- flushInFlight = Promise.resolve().then(async () => {
229
+ return Promise.resolve().then(async () => {
230
230
  if (batchSink) {
231
231
  await batchSink(events);
232
232
  } else {
@@ -238,28 +238,47 @@ function createTrackingClient(opts) {
238
238
  succeeded = true;
239
239
  }).catch(() => {
240
240
  buffer.unshift(...events.slice(sent));
241
- }).finally(() => {
241
+ }).then(() => {
242
+ if (succeeded && buffer.length > 0 && !disposed) {
243
+ return runFlush();
244
+ }
245
+ });
246
+ };
247
+ const flush = () => {
248
+ if (disposed) return Promise.resolve();
249
+ if (flushInFlight) return flushInFlight;
250
+ if (!buffer.length) return Promise.resolve();
251
+ flushInFlight = runFlush().finally(() => {
242
252
  flushInFlight = null;
243
- if (succeeded && !disposed && buffer.length > 0) flush();
244
253
  });
254
+ return flushInFlight;
245
255
  };
246
- intervalId = flushIntervalMs > 0 ? globalThis.setInterval(flush, flushIntervalMs) : void 0;
256
+ const drainAll = async () => {
257
+ await flush();
258
+ while (buffer.length > 0) {
259
+ await flush();
260
+ }
261
+ };
262
+ intervalId = flushIntervalMs > 0 ? globalThis.setInterval(() => void flush(), flushIntervalMs) : void 0;
247
263
  intervalId?.unref?.();
248
264
  return {
249
265
  track: (event) => {
250
- if (disposed) return;
266
+ if (disposed || disposing) return;
251
267
  buffer.push(event);
252
- if (buffer.length >= maxBatchSize) flush();
268
+ if (buffer.length >= maxBatchSize) void flush();
253
269
  },
254
270
  flush,
255
271
  dispose: () => {
256
- if (disposed) return;
272
+ if (disposed || disposing) return Promise.resolve();
273
+ disposing = true;
257
274
  if (intervalId !== void 0) {
258
275
  globalThis.clearInterval(intervalId);
259
276
  intervalId = void 0;
260
277
  }
261
- flush();
262
- disposed = true;
278
+ return drainAll().finally(() => {
279
+ disposed = true;
280
+ disposing = false;
281
+ });
263
282
  }
264
283
  };
265
284
  }
@@ -317,6 +336,12 @@ function createPluginHost(plugins = []) {
317
336
  }
318
337
  return filtered;
319
338
  };
339
+ const deliverTelemetryBatch = (events, ctx) => {
340
+ for (const plugin of list) {
341
+ plugin.onTelemetryBatch?.(events, ctx);
342
+ }
343
+ return events;
344
+ };
320
345
  const composeTrackingSink = (sink, ctx) => {
321
346
  let composed = sink;
322
347
  for (const plugin of list) {
@@ -339,6 +364,7 @@ function createPluginHost(plugins = []) {
339
364
  disposeAll,
340
365
  runTelemetry,
341
366
  runTelemetryBatch,
367
+ deliverTelemetryBatch,
342
368
  composeTrackingSink,
343
369
  scoreAssessment
344
370
  };
package/dist/index.d.cts CHANGED
@@ -113,8 +113,8 @@ type TelemetrySink = (event: TelemetryEvent) => void | Promise<void>;
113
113
  type TelemetryBatchSink = (events: TelemetryEvent[]) => void | Promise<void>;
114
114
  type TrackingClient = {
115
115
  track: (event: TelemetryEvent) => void;
116
- flush?: () => void;
117
- dispose?: () => void;
116
+ flush?: () => void | Promise<void>;
117
+ dispose?: () => void | Promise<void>;
118
118
  };
119
119
 
120
120
  declare const telemetryCatalogVersion: 1;
@@ -198,6 +198,8 @@ type PluginHost = {
198
198
  disposeAll: () => void;
199
199
  runTelemetry: (event: TelemetryEvent, ctx: LessonkitPluginContext) => TelemetryEvent | null;
200
200
  runTelemetryBatch: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => TelemetryEvent[];
201
+ /** Invoke only `onTelemetryBatch` hooks; events were already filtered at emit time. */
202
+ deliverTelemetryBatch: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => TelemetryEvent[];
201
203
  composeTrackingSink: (sink: TelemetrySink | undefined, ctx: LessonkitPluginContext) => TelemetrySink | undefined;
202
204
  scoreAssessment: (input: AssessmentScoreInput, ctx: LessonkitPluginContext) => AssessmentScoreResult | null;
203
205
  };
package/dist/index.d.ts CHANGED
@@ -113,8 +113,8 @@ type TelemetrySink = (event: TelemetryEvent) => void | Promise<void>;
113
113
  type TelemetryBatchSink = (events: TelemetryEvent[]) => void | Promise<void>;
114
114
  type TrackingClient = {
115
115
  track: (event: TelemetryEvent) => void;
116
- flush?: () => void;
117
- dispose?: () => void;
116
+ flush?: () => void | Promise<void>;
117
+ dispose?: () => void | Promise<void>;
118
118
  };
119
119
 
120
120
  declare const telemetryCatalogVersion: 1;
@@ -198,6 +198,8 @@ type PluginHost = {
198
198
  disposeAll: () => void;
199
199
  runTelemetry: (event: TelemetryEvent, ctx: LessonkitPluginContext) => TelemetryEvent | null;
200
200
  runTelemetryBatch: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => TelemetryEvent[];
201
+ /** Invoke only `onTelemetryBatch` hooks; events were already filtered at emit time. */
202
+ deliverTelemetryBatch: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => TelemetryEvent[];
201
203
  composeTrackingSink: (sink: TelemetrySink | undefined, ctx: LessonkitPluginContext) => TelemetrySink | undefined;
202
204
  scoreAssessment: (input: AssessmentScoreInput, ctx: LessonkitPluginContext) => AssessmentScoreResult | null;
203
205
  };
package/dist/index.js CHANGED
@@ -179,14 +179,14 @@ function createTrackingClient(opts) {
179
179
  const buffer = [];
180
180
  let flushInFlight = null;
181
181
  let disposed = false;
182
+ let disposing = false;
182
183
  let intervalId;
183
- const flush = () => {
184
- if (disposed || flushInFlight) return;
185
- if (!buffer.length) return;
184
+ const runFlush = () => {
185
+ if (!buffer.length) return Promise.resolve();
186
186
  const events = buffer.splice(0, buffer.length);
187
187
  let sent = 0;
188
188
  let succeeded = false;
189
- flushInFlight = Promise.resolve().then(async () => {
189
+ return Promise.resolve().then(async () => {
190
190
  if (batchSink) {
191
191
  await batchSink(events);
192
192
  } else {
@@ -198,28 +198,47 @@ function createTrackingClient(opts) {
198
198
  succeeded = true;
199
199
  }).catch(() => {
200
200
  buffer.unshift(...events.slice(sent));
201
- }).finally(() => {
201
+ }).then(() => {
202
+ if (succeeded && buffer.length > 0 && !disposed) {
203
+ return runFlush();
204
+ }
205
+ });
206
+ };
207
+ const flush = () => {
208
+ if (disposed) return Promise.resolve();
209
+ if (flushInFlight) return flushInFlight;
210
+ if (!buffer.length) return Promise.resolve();
211
+ flushInFlight = runFlush().finally(() => {
202
212
  flushInFlight = null;
203
- if (succeeded && !disposed && buffer.length > 0) flush();
204
213
  });
214
+ return flushInFlight;
205
215
  };
206
- intervalId = flushIntervalMs > 0 ? globalThis.setInterval(flush, flushIntervalMs) : void 0;
216
+ const drainAll = async () => {
217
+ await flush();
218
+ while (buffer.length > 0) {
219
+ await flush();
220
+ }
221
+ };
222
+ intervalId = flushIntervalMs > 0 ? globalThis.setInterval(() => void flush(), flushIntervalMs) : void 0;
207
223
  intervalId?.unref?.();
208
224
  return {
209
225
  track: (event) => {
210
- if (disposed) return;
226
+ if (disposed || disposing) return;
211
227
  buffer.push(event);
212
- if (buffer.length >= maxBatchSize) flush();
228
+ if (buffer.length >= maxBatchSize) void flush();
213
229
  },
214
230
  flush,
215
231
  dispose: () => {
216
- if (disposed) return;
232
+ if (disposed || disposing) return Promise.resolve();
233
+ disposing = true;
217
234
  if (intervalId !== void 0) {
218
235
  globalThis.clearInterval(intervalId);
219
236
  intervalId = void 0;
220
237
  }
221
- flush();
222
- disposed = true;
238
+ return drainAll().finally(() => {
239
+ disposed = true;
240
+ disposing = false;
241
+ });
223
242
  }
224
243
  };
225
244
  }
@@ -277,6 +296,12 @@ function createPluginHost(plugins = []) {
277
296
  }
278
297
  return filtered;
279
298
  };
299
+ const deliverTelemetryBatch = (events, ctx) => {
300
+ for (const plugin of list) {
301
+ plugin.onTelemetryBatch?.(events, ctx);
302
+ }
303
+ return events;
304
+ };
280
305
  const composeTrackingSink = (sink, ctx) => {
281
306
  let composed = sink;
282
307
  for (const plugin of list) {
@@ -299,6 +324,7 @@ function createPluginHost(plugins = []) {
299
324
  disposeAll,
300
325
  runTelemetry,
301
326
  runTelemetryBatch,
327
+ deliverTelemetryBatch,
302
328
  composeTrackingSink,
303
329
  scoreAssessment
304
330
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lessonkit/core",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "private": false,
5
5
  "description": "Shared types and telemetry primitives for LessonKit.",
6
6
  "license": "Apache-2.0",