@lessonkit/core 0.9.1 → 0.9.3
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 +56 -15
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +56 -15
- package/package.json +1 -1
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
|
|
224
|
-
if (
|
|
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
|
-
|
|
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
|
-
}).
|
|
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
|
-
|
|
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
|
-
|
|
262
|
-
|
|
278
|
+
return drainAll().finally(() => {
|
|
279
|
+
disposed = true;
|
|
280
|
+
disposing = false;
|
|
281
|
+
});
|
|
263
282
|
}
|
|
264
283
|
};
|
|
265
284
|
}
|
|
@@ -317,11 +336,32 @@ function createPluginHost(plugins = []) {
|
|
|
317
336
|
}
|
|
318
337
|
return filtered;
|
|
319
338
|
};
|
|
320
|
-
const
|
|
339
|
+
const deliverTelemetryBatch = (events, ctx) => {
|
|
340
|
+
for (const plugin of list) {
|
|
341
|
+
plugin.onTelemetryBatch?.(events, ctx);
|
|
342
|
+
}
|
|
343
|
+
return events;
|
|
344
|
+
};
|
|
345
|
+
const composeTrackingSink = (sink, ctxSource) => {
|
|
346
|
+
if (!sink) return void 0;
|
|
347
|
+
const resolveCtx = () => typeof ctxSource === "function" ? ctxSource() : ctxSource;
|
|
348
|
+
const ctxKey = (ctx) => `${ctx.courseId}\0${ctx.sessionId ?? ""}\0${ctx.attemptId ?? ""}`;
|
|
349
|
+
const layers = [];
|
|
321
350
|
let composed = sink;
|
|
322
351
|
for (const plugin of list) {
|
|
323
|
-
if (!plugin.wrapTrackingSink
|
|
324
|
-
|
|
352
|
+
if (!plugin.wrapTrackingSink) continue;
|
|
353
|
+
const inner = composed;
|
|
354
|
+
const layer = { plugin, inner, wrapped: null, lastCtxKey: "" };
|
|
355
|
+
layers.push(layer);
|
|
356
|
+
composed = (event) => {
|
|
357
|
+
const ctx = resolveCtx();
|
|
358
|
+
const key = ctxKey(ctx);
|
|
359
|
+
if (!layer.wrapped || layer.lastCtxKey !== key) {
|
|
360
|
+
layer.wrapped = layer.plugin.wrapTrackingSink(layer.inner, ctx) ?? layer.inner;
|
|
361
|
+
layer.lastCtxKey = key;
|
|
362
|
+
}
|
|
363
|
+
return layer.wrapped(event);
|
|
364
|
+
};
|
|
325
365
|
}
|
|
326
366
|
return composed;
|
|
327
367
|
};
|
|
@@ -339,6 +379,7 @@ function createPluginHost(plugins = []) {
|
|
|
339
379
|
disposeAll,
|
|
340
380
|
runTelemetry,
|
|
341
381
|
runTelemetryBatch,
|
|
382
|
+
deliverTelemetryBatch,
|
|
342
383
|
composeTrackingSink,
|
|
343
384
|
scoreAssessment
|
|
344
385
|
};
|
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,7 +198,9 @@ type PluginHost = {
|
|
|
198
198
|
disposeAll: () => void;
|
|
199
199
|
runTelemetry: (event: TelemetryEvent, ctx: LessonkitPluginContext) => TelemetryEvent | null;
|
|
200
200
|
runTelemetryBatch: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => TelemetryEvent[];
|
|
201
|
-
|
|
201
|
+
/** Invoke only `onTelemetryBatch` hooks; events were already filtered at emit time. */
|
|
202
|
+
deliverTelemetryBatch: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => TelemetryEvent[];
|
|
203
|
+
composeTrackingSink: (sink: TelemetrySink | undefined, ctx: LessonkitPluginContext | (() => LessonkitPluginContext)) => TelemetrySink | undefined;
|
|
202
204
|
scoreAssessment: (input: AssessmentScoreInput, ctx: LessonkitPluginContext) => AssessmentScoreResult | null;
|
|
203
205
|
};
|
|
204
206
|
declare function defineLessonkitPlugin(plugin: LessonkitPlugin): LessonkitPlugin;
|
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,7 +198,9 @@ type PluginHost = {
|
|
|
198
198
|
disposeAll: () => void;
|
|
199
199
|
runTelemetry: (event: TelemetryEvent, ctx: LessonkitPluginContext) => TelemetryEvent | null;
|
|
200
200
|
runTelemetryBatch: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => TelemetryEvent[];
|
|
201
|
-
|
|
201
|
+
/** Invoke only `onTelemetryBatch` hooks; events were already filtered at emit time. */
|
|
202
|
+
deliverTelemetryBatch: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => TelemetryEvent[];
|
|
203
|
+
composeTrackingSink: (sink: TelemetrySink | undefined, ctx: LessonkitPluginContext | (() => LessonkitPluginContext)) => TelemetrySink | undefined;
|
|
202
204
|
scoreAssessment: (input: AssessmentScoreInput, ctx: LessonkitPluginContext) => AssessmentScoreResult | null;
|
|
203
205
|
};
|
|
204
206
|
declare function defineLessonkitPlugin(plugin: LessonkitPlugin): LessonkitPlugin;
|
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
|
|
184
|
-
if (
|
|
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
|
-
|
|
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
|
-
}).
|
|
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
|
-
|
|
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
|
-
|
|
222
|
-
|
|
238
|
+
return drainAll().finally(() => {
|
|
239
|
+
disposed = true;
|
|
240
|
+
disposing = false;
|
|
241
|
+
});
|
|
223
242
|
}
|
|
224
243
|
};
|
|
225
244
|
}
|
|
@@ -277,11 +296,32 @@ function createPluginHost(plugins = []) {
|
|
|
277
296
|
}
|
|
278
297
|
return filtered;
|
|
279
298
|
};
|
|
280
|
-
const
|
|
299
|
+
const deliverTelemetryBatch = (events, ctx) => {
|
|
300
|
+
for (const plugin of list) {
|
|
301
|
+
plugin.onTelemetryBatch?.(events, ctx);
|
|
302
|
+
}
|
|
303
|
+
return events;
|
|
304
|
+
};
|
|
305
|
+
const composeTrackingSink = (sink, ctxSource) => {
|
|
306
|
+
if (!sink) return void 0;
|
|
307
|
+
const resolveCtx = () => typeof ctxSource === "function" ? ctxSource() : ctxSource;
|
|
308
|
+
const ctxKey = (ctx) => `${ctx.courseId}\0${ctx.sessionId ?? ""}\0${ctx.attemptId ?? ""}`;
|
|
309
|
+
const layers = [];
|
|
281
310
|
let composed = sink;
|
|
282
311
|
for (const plugin of list) {
|
|
283
|
-
if (!plugin.wrapTrackingSink
|
|
284
|
-
|
|
312
|
+
if (!plugin.wrapTrackingSink) continue;
|
|
313
|
+
const inner = composed;
|
|
314
|
+
const layer = { plugin, inner, wrapped: null, lastCtxKey: "" };
|
|
315
|
+
layers.push(layer);
|
|
316
|
+
composed = (event) => {
|
|
317
|
+
const ctx = resolveCtx();
|
|
318
|
+
const key = ctxKey(ctx);
|
|
319
|
+
if (!layer.wrapped || layer.lastCtxKey !== key) {
|
|
320
|
+
layer.wrapped = layer.plugin.wrapTrackingSink(layer.inner, ctx) ?? layer.inner;
|
|
321
|
+
layer.lastCtxKey = key;
|
|
322
|
+
}
|
|
323
|
+
return layer.wrapped(event);
|
|
324
|
+
};
|
|
285
325
|
}
|
|
286
326
|
return composed;
|
|
287
327
|
};
|
|
@@ -299,6 +339,7 @@ function createPluginHost(plugins = []) {
|
|
|
299
339
|
disposeAll,
|
|
300
340
|
runTelemetry,
|
|
301
341
|
runTelemetryBatch,
|
|
342
|
+
deliverTelemetryBatch,
|
|
302
343
|
composeTrackingSink,
|
|
303
344
|
scoreAssessment
|
|
304
345
|
};
|