@lessonkit/core 0.8.1 → 0.9.1

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
@@ -26,8 +26,10 @@ __export(index_exports, {
26
26
  assertValidId: () => assertValidId,
27
27
  buildLessonkitUrn: () => buildLessonkitUrn,
28
28
  buildTelemetryCatalog: () => buildTelemetryCatalog,
29
+ createPluginHost: () => createPluginHost,
29
30
  createSessionId: () => createSessionId,
30
31
  createTrackingClient: () => createTrackingClient,
32
+ defineLessonkitPlugin: () => defineLessonkitPlugin,
31
33
  deriveId: () => deriveId,
32
34
  nowIso: () => nowIso,
33
35
  slugifyId: () => slugifyId,
@@ -273,6 +275,74 @@ function createSessionId() {
273
275
  function nowIso() {
274
276
  return (/* @__PURE__ */ new Date()).toISOString();
275
277
  }
278
+
279
+ // src/plugins.ts
280
+ function defineLessonkitPlugin(plugin) {
281
+ return plugin;
282
+ }
283
+ function warnDuplicatePlugin(id) {
284
+ const g = globalThis;
285
+ if (typeof g.process !== "undefined" && g.process.env?.NODE_ENV === "production") return;
286
+ console.warn(`[lessonkit] plugin id "${id}" was registered more than once; using the latest definition`);
287
+ }
288
+ function createPluginHost(plugins = []) {
289
+ const registry = /* @__PURE__ */ new Map();
290
+ for (const plugin of plugins) {
291
+ if (registry.has(plugin.id)) warnDuplicatePlugin(plugin.id);
292
+ registry.set(plugin.id, plugin);
293
+ }
294
+ const list = [...registry.values()];
295
+ const setupAll = (ctx) => {
296
+ for (const plugin of list) {
297
+ plugin.setup?.(ctx);
298
+ }
299
+ };
300
+ const disposeAll = () => {
301
+ for (let i = list.length - 1; i >= 0; i -= 1) {
302
+ list[i]?.dispose?.();
303
+ }
304
+ };
305
+ const runTelemetry = (event, ctx) => {
306
+ let current = event;
307
+ for (const plugin of list) {
308
+ if (!plugin.onTelemetry || current === null) continue;
309
+ current = plugin.onTelemetry(current, ctx);
310
+ }
311
+ return current;
312
+ };
313
+ const runTelemetryBatch = (events, ctx) => {
314
+ const filtered = events.map((event) => runTelemetry(event, ctx)).filter((event) => event !== null);
315
+ for (const plugin of list) {
316
+ plugin.onTelemetryBatch?.(filtered, ctx);
317
+ }
318
+ return filtered;
319
+ };
320
+ const composeTrackingSink = (sink, ctx) => {
321
+ let composed = sink;
322
+ for (const plugin of list) {
323
+ if (!plugin.wrapTrackingSink || !composed) continue;
324
+ composed = plugin.wrapTrackingSink(composed, ctx);
325
+ }
326
+ return composed;
327
+ };
328
+ const scoreAssessment = (input, ctx) => {
329
+ for (const plugin of list) {
330
+ if (plugin.kind !== "assessment" || !plugin.scoreAssessment) continue;
331
+ const result = plugin.scoreAssessment(input, ctx);
332
+ if (result) return result;
333
+ }
334
+ return null;
335
+ };
336
+ return {
337
+ plugins: list,
338
+ setupAll,
339
+ disposeAll,
340
+ runTelemetry,
341
+ runTelemetryBatch,
342
+ composeTrackingSink,
343
+ scoreAssessment
344
+ };
345
+ }
276
346
  // Annotate the CommonJS export names for ESM import in node:
277
347
  0 && (module.exports = {
278
348
  ID_MAX_LENGTH,
@@ -281,8 +351,10 @@ function nowIso() {
281
351
  assertValidId,
282
352
  buildLessonkitUrn,
283
353
  buildTelemetryCatalog,
354
+ createPluginHost,
284
355
  createSessionId,
285
356
  createTrackingClient,
357
+ defineLessonkitPlugin,
286
358
  deriveId,
287
359
  nowIso,
288
360
  slugifyId,
package/dist/index.d.cts CHANGED
@@ -143,4 +143,65 @@ declare function createSessionId(): string;
143
143
 
144
144
  declare function nowIso(): string;
145
145
 
146
- export { type BlockId, type CheckId, type CourseId, ID_MAX_LENGTH, ID_PATTERN, type IdentityValidationIssue, type IdentityValidationResult, type InteractionData, type LessonId, type LessonLifecycleData, type LessonkitUrnParts, type QuizAnsweredData, type QuizCompletedData, TELEMETRY_EVENT_CATALOG, type TelemetryBatchSink, type TelemetryCatalogEntry, type TelemetryEvent, type TelemetryEventBase, type TelemetryEventName, type TelemetrySink, type TelemetryUser, type TrackingClient, assertValidId, buildLessonkitUrn, buildTelemetryCatalog, createSessionId, createTrackingClient, deriveId, nowIso, slugifyId, telemetryCatalogVersion, validateId };
146
+ /** Plugin category aligns with roadmap extension areas. */
147
+ type LessonkitPluginKind = "analytics" | "lms" | "assessment" | "interaction" | "ai";
148
+ type LessonkitPluginContext = {
149
+ courseId: CourseId;
150
+ sessionId?: string;
151
+ attemptId?: string;
152
+ };
153
+ type AssessmentScoreInput = {
154
+ checkId: string;
155
+ lessonId?: string;
156
+ response: unknown;
157
+ };
158
+ type AssessmentScoreResult = {
159
+ score: number;
160
+ maxScore?: number;
161
+ passed?: boolean;
162
+ feedback?: string;
163
+ };
164
+ /** Metadata for custom interaction blocks (renderer wiring stays in app code until 1.0). */
165
+ type InteractionBlockRegistration = {
166
+ blockType: string;
167
+ catalogVersion?: string;
168
+ description?: string;
169
+ };
170
+ /**
171
+ * Framework plugin contract (v1). Plugins are plain objects registered on `LessonkitProvider`.
172
+ * Marketplace / dynamic loading are out of scope until Studio post-1.0.
173
+ */
174
+ type LessonkitPlugin = {
175
+ id: string;
176
+ version: string;
177
+ kind: LessonkitPluginKind;
178
+ name?: string;
179
+ setup?: (ctx: LessonkitPluginContext) => void;
180
+ dispose?: () => void;
181
+ /**
182
+ * Observe or filter telemetry before tracking/xAPI. Return `null` to drop the event.
183
+ * Hooks run in registration order.
184
+ */
185
+ onTelemetry?: (event: TelemetryEvent, ctx: LessonkitPluginContext) => TelemetryEvent | null;
186
+ /** Optional batch observer (analytics); receives events after per-event hooks. */
187
+ onTelemetryBatch?: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => void;
188
+ /** Wrap the configured tracking sink (analytics plugins). First registered = innermost. */
189
+ wrapTrackingSink?: (sink: TelemetrySink, ctx: LessonkitPluginContext) => TelemetrySink;
190
+ /** Optional assessment scoring override (assessment plugins). */
191
+ scoreAssessment?: (input: AssessmentScoreInput, ctx: LessonkitPluginContext) => AssessmentScoreResult | null;
192
+ /** Declare custom interaction block types for generators/tooling. */
193
+ interactionBlocks?: InteractionBlockRegistration[];
194
+ };
195
+ type PluginHost = {
196
+ readonly plugins: readonly LessonkitPlugin[];
197
+ setupAll: (ctx: LessonkitPluginContext) => void;
198
+ disposeAll: () => void;
199
+ runTelemetry: (event: TelemetryEvent, ctx: LessonkitPluginContext) => TelemetryEvent | null;
200
+ runTelemetryBatch: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => TelemetryEvent[];
201
+ composeTrackingSink: (sink: TelemetrySink | undefined, ctx: LessonkitPluginContext) => TelemetrySink | undefined;
202
+ scoreAssessment: (input: AssessmentScoreInput, ctx: LessonkitPluginContext) => AssessmentScoreResult | null;
203
+ };
204
+ declare function defineLessonkitPlugin(plugin: LessonkitPlugin): LessonkitPlugin;
205
+ declare function createPluginHost(plugins?: readonly LessonkitPlugin[]): PluginHost;
206
+
207
+ export { type AssessmentScoreInput, type AssessmentScoreResult, type BlockId, type CheckId, type CourseId, ID_MAX_LENGTH, ID_PATTERN, type IdentityValidationIssue, type IdentityValidationResult, type InteractionBlockRegistration, type InteractionData, type LessonId, type LessonLifecycleData, type LessonkitPlugin, type LessonkitPluginContext, type LessonkitPluginKind, type LessonkitUrnParts, type PluginHost, type QuizAnsweredData, type QuizCompletedData, TELEMETRY_EVENT_CATALOG, type TelemetryBatchSink, type TelemetryCatalogEntry, type TelemetryEvent, type TelemetryEventBase, type TelemetryEventName, type TelemetrySink, type TelemetryUser, type TrackingClient, assertValidId, buildLessonkitUrn, buildTelemetryCatalog, createPluginHost, createSessionId, createTrackingClient, defineLessonkitPlugin, deriveId, nowIso, slugifyId, telemetryCatalogVersion, validateId };
package/dist/index.d.ts CHANGED
@@ -143,4 +143,65 @@ declare function createSessionId(): string;
143
143
 
144
144
  declare function nowIso(): string;
145
145
 
146
- export { type BlockId, type CheckId, type CourseId, ID_MAX_LENGTH, ID_PATTERN, type IdentityValidationIssue, type IdentityValidationResult, type InteractionData, type LessonId, type LessonLifecycleData, type LessonkitUrnParts, type QuizAnsweredData, type QuizCompletedData, TELEMETRY_EVENT_CATALOG, type TelemetryBatchSink, type TelemetryCatalogEntry, type TelemetryEvent, type TelemetryEventBase, type TelemetryEventName, type TelemetrySink, type TelemetryUser, type TrackingClient, assertValidId, buildLessonkitUrn, buildTelemetryCatalog, createSessionId, createTrackingClient, deriveId, nowIso, slugifyId, telemetryCatalogVersion, validateId };
146
+ /** Plugin category aligns with roadmap extension areas. */
147
+ type LessonkitPluginKind = "analytics" | "lms" | "assessment" | "interaction" | "ai";
148
+ type LessonkitPluginContext = {
149
+ courseId: CourseId;
150
+ sessionId?: string;
151
+ attemptId?: string;
152
+ };
153
+ type AssessmentScoreInput = {
154
+ checkId: string;
155
+ lessonId?: string;
156
+ response: unknown;
157
+ };
158
+ type AssessmentScoreResult = {
159
+ score: number;
160
+ maxScore?: number;
161
+ passed?: boolean;
162
+ feedback?: string;
163
+ };
164
+ /** Metadata for custom interaction blocks (renderer wiring stays in app code until 1.0). */
165
+ type InteractionBlockRegistration = {
166
+ blockType: string;
167
+ catalogVersion?: string;
168
+ description?: string;
169
+ };
170
+ /**
171
+ * Framework plugin contract (v1). Plugins are plain objects registered on `LessonkitProvider`.
172
+ * Marketplace / dynamic loading are out of scope until Studio post-1.0.
173
+ */
174
+ type LessonkitPlugin = {
175
+ id: string;
176
+ version: string;
177
+ kind: LessonkitPluginKind;
178
+ name?: string;
179
+ setup?: (ctx: LessonkitPluginContext) => void;
180
+ dispose?: () => void;
181
+ /**
182
+ * Observe or filter telemetry before tracking/xAPI. Return `null` to drop the event.
183
+ * Hooks run in registration order.
184
+ */
185
+ onTelemetry?: (event: TelemetryEvent, ctx: LessonkitPluginContext) => TelemetryEvent | null;
186
+ /** Optional batch observer (analytics); receives events after per-event hooks. */
187
+ onTelemetryBatch?: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => void;
188
+ /** Wrap the configured tracking sink (analytics plugins). First registered = innermost. */
189
+ wrapTrackingSink?: (sink: TelemetrySink, ctx: LessonkitPluginContext) => TelemetrySink;
190
+ /** Optional assessment scoring override (assessment plugins). */
191
+ scoreAssessment?: (input: AssessmentScoreInput, ctx: LessonkitPluginContext) => AssessmentScoreResult | null;
192
+ /** Declare custom interaction block types for generators/tooling. */
193
+ interactionBlocks?: InteractionBlockRegistration[];
194
+ };
195
+ type PluginHost = {
196
+ readonly plugins: readonly LessonkitPlugin[];
197
+ setupAll: (ctx: LessonkitPluginContext) => void;
198
+ disposeAll: () => void;
199
+ runTelemetry: (event: TelemetryEvent, ctx: LessonkitPluginContext) => TelemetryEvent | null;
200
+ runTelemetryBatch: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => TelemetryEvent[];
201
+ composeTrackingSink: (sink: TelemetrySink | undefined, ctx: LessonkitPluginContext) => TelemetrySink | undefined;
202
+ scoreAssessment: (input: AssessmentScoreInput, ctx: LessonkitPluginContext) => AssessmentScoreResult | null;
203
+ };
204
+ declare function defineLessonkitPlugin(plugin: LessonkitPlugin): LessonkitPlugin;
205
+ declare function createPluginHost(plugins?: readonly LessonkitPlugin[]): PluginHost;
206
+
207
+ export { type AssessmentScoreInput, type AssessmentScoreResult, type BlockId, type CheckId, type CourseId, ID_MAX_LENGTH, ID_PATTERN, type IdentityValidationIssue, type IdentityValidationResult, type InteractionBlockRegistration, type InteractionData, type LessonId, type LessonLifecycleData, type LessonkitPlugin, type LessonkitPluginContext, type LessonkitPluginKind, type LessonkitUrnParts, type PluginHost, type QuizAnsweredData, type QuizCompletedData, TELEMETRY_EVENT_CATALOG, type TelemetryBatchSink, type TelemetryCatalogEntry, type TelemetryEvent, type TelemetryEventBase, type TelemetryEventName, type TelemetrySink, type TelemetryUser, type TrackingClient, assertValidId, buildLessonkitUrn, buildTelemetryCatalog, createPluginHost, createSessionId, createTrackingClient, defineLessonkitPlugin, deriveId, nowIso, slugifyId, telemetryCatalogVersion, validateId };
package/dist/index.js CHANGED
@@ -235,6 +235,74 @@ function createSessionId() {
235
235
  function nowIso() {
236
236
  return (/* @__PURE__ */ new Date()).toISOString();
237
237
  }
238
+
239
+ // src/plugins.ts
240
+ function defineLessonkitPlugin(plugin) {
241
+ return plugin;
242
+ }
243
+ function warnDuplicatePlugin(id) {
244
+ const g = globalThis;
245
+ if (typeof g.process !== "undefined" && g.process.env?.NODE_ENV === "production") return;
246
+ console.warn(`[lessonkit] plugin id "${id}" was registered more than once; using the latest definition`);
247
+ }
248
+ function createPluginHost(plugins = []) {
249
+ const registry = /* @__PURE__ */ new Map();
250
+ for (const plugin of plugins) {
251
+ if (registry.has(plugin.id)) warnDuplicatePlugin(plugin.id);
252
+ registry.set(plugin.id, plugin);
253
+ }
254
+ const list = [...registry.values()];
255
+ const setupAll = (ctx) => {
256
+ for (const plugin of list) {
257
+ plugin.setup?.(ctx);
258
+ }
259
+ };
260
+ const disposeAll = () => {
261
+ for (let i = list.length - 1; i >= 0; i -= 1) {
262
+ list[i]?.dispose?.();
263
+ }
264
+ };
265
+ const runTelemetry = (event, ctx) => {
266
+ let current = event;
267
+ for (const plugin of list) {
268
+ if (!plugin.onTelemetry || current === null) continue;
269
+ current = plugin.onTelemetry(current, ctx);
270
+ }
271
+ return current;
272
+ };
273
+ const runTelemetryBatch = (events, ctx) => {
274
+ const filtered = events.map((event) => runTelemetry(event, ctx)).filter((event) => event !== null);
275
+ for (const plugin of list) {
276
+ plugin.onTelemetryBatch?.(filtered, ctx);
277
+ }
278
+ return filtered;
279
+ };
280
+ const composeTrackingSink = (sink, ctx) => {
281
+ let composed = sink;
282
+ for (const plugin of list) {
283
+ if (!plugin.wrapTrackingSink || !composed) continue;
284
+ composed = plugin.wrapTrackingSink(composed, ctx);
285
+ }
286
+ return composed;
287
+ };
288
+ const scoreAssessment = (input, ctx) => {
289
+ for (const plugin of list) {
290
+ if (plugin.kind !== "assessment" || !plugin.scoreAssessment) continue;
291
+ const result = plugin.scoreAssessment(input, ctx);
292
+ if (result) return result;
293
+ }
294
+ return null;
295
+ };
296
+ return {
297
+ plugins: list,
298
+ setupAll,
299
+ disposeAll,
300
+ runTelemetry,
301
+ runTelemetryBatch,
302
+ composeTrackingSink,
303
+ scoreAssessment
304
+ };
305
+ }
238
306
  export {
239
307
  ID_MAX_LENGTH,
240
308
  ID_PATTERN,
@@ -242,8 +310,10 @@ export {
242
310
  assertValidId,
243
311
  buildLessonkitUrn,
244
312
  buildTelemetryCatalog,
313
+ createPluginHost,
245
314
  createSessionId,
246
315
  createTrackingClient,
316
+ defineLessonkitPlugin,
247
317
  deriveId,
248
318
  nowIso,
249
319
  slugifyId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lessonkit/core",
3
- "version": "0.8.1",
3
+ "version": "0.9.1",
4
4
  "private": false,
5
5
  "description": "Shared types and telemetry primitives for LessonKit.",
6
6
  "license": "Apache-2.0",