TinCanJS (Experience API Library)
TinCanJS is a JavaScript library designed for implementing the Experience API (xAPI, formerly Tin Can API) in both web browser and Node.js environments. The current stable version is 0.50.0. While there isn't a fixed release cadence, the project sees regular updates addressing bug fixes, enhancing LRS interaction (e.g., retrieving activity or profile data), and adding new features like end-to-end statement attachment support. Key differentiators include its broad browser compatibility, supporting older IE versions (IE8+ for CORS, IE6+ for non-CORS) and modern browsers, as well as providing a unified API for cross-origin requests. It abstracts away environmental differences, offering a consistent interface for constructing, sending, and retrieving xAPI statements and interacting with Learning Record Stores (LRSs).
Common errors
-
ReferenceError: TinCan is not defined
cause The TinCanJS library script was not successfully loaded in the browser environment, or the global `TinCan` object was accessed before the script fully executed.fixEnsure `<script src="build/tincan-min.js"></script>` is included in your HTML document before any code attempts to use `TinCan`, typically at the end of the `<body>` tag. -
Error: Cannot find module 'xhr2'
cause The `xhr2` npm package, which is a required peer dependency for TinCanJS to function in Node.js environments, is not installed.fixInstall the `xhr2` module by running `npm install xhr2` in your project's root directory. -
TypeError: TinCan.LRS is not a constructor
cause This error typically occurs when trying to instantiate `LRS` or other core objects before `TinCan` has been properly loaded and assigned, or if attempting to use ES Module import syntax (e.g., `import { LRS } from 'tincanjs'`) which is not supported by this library.fixFor Node.js, ensure `const TinCan = require('tincanjs');` is at the top of your file. For browsers, verify the `<script>` tag loading `tincanjs` is present and executed. Always access constructors as properties of the `TinCan` object, e.g., `new TinCan.LRS(...)`. -
Access to XMLHttpRequest at '...' from origin '...' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
cause The Learning Record Store (LRS) endpoint you are attempting to connect to does not have the necessary Cross-Origin Resource Sharing (CORS) headers configured to allow requests from your web application's origin.fixThis is an LRS server-side configuration issue, not a client-side library problem. Contact your LRS administrator to ensure that appropriate 'Access-Control-Allow-Origin' headers are configured on the LRS to permit requests from your application's domain.
Warnings
- gotcha The Node.js environment implementation of TinCanJS has a mandatory dependency on the 'xhr2' package for consistent LRS request APIs. It also does not support synchronous LRS requests due to this underlying dependency.
- gotcha Supporting older browser versions (e.g., pre-IE10 or specific legacy Chrome/Firefox/Safari versions) may require polyfills for features like TypedArrays, ArrayBuffer with slice, Blob, and TextDecoder/TextEncoder. The default build includes these, but they can be removed to reduce bundle size if targeting modern browsers exclusively.
- breaking Version 0.34.0 introduced a regression in how configured Content-Type headers were handled for LRS requests, potentially leading to incorrect request headers or LRS rejection. This issue was subsequently fixed in version 0.34.1.
- gotcha Prior to version 0.41.1, LRS objects instantiated without an explicit version property could experience issues when querying statements, particularly when dealing with newer xAPI versions like '1.0.2', due to missing compatibility table entries.
- gotcha The build system was converted to Grunt in version 0.30.0, and the minifier was upgraded. This change, while not intended to alter interfaces or fix bugs, carried a risk of subtle behavioral differences in the minified files, necessitating thorough testing if upgrading from pre-0.30.0 versions and using minified builds.
Install
-
npm install tincanjs -
yarn add tincanjs -
pnpm add tincanjs
Imports
- TinCan
import TinCan from 'tincanjs';
const TinCan = require('tincanjs'); - TinCan
import { TinCan } from 'tincanjs';<script src="build/tincan-min.js"></script> // Then access `TinCan` globally
- LRS
import { LRS } from 'tincanjs';const lrs = new TinCan.LRS({ ... });
Quickstart
const TinCan = require('tincanjs');
const lrs = new TinCan.LRS({
endpoint: process.env.LRS_ENDPOINT ?? 'https://cloud.scorm.com/tc/public/', // Example endpoint
username: process.env.LRS_USERNAME ?? 'YOUR_USERNAME',
password: process.env.LRS_PASSWORD ?? 'YOUR_PASSWORD',
version: '1.0.3' // Specify the xAPI version, e.g., '1.0.3'
});
const actor = new TinCan.Agent({
name: 'Test User',
mbox: 'mailto:test.user@example.com'
});
const verb = new TinCan.Verb({
id: 'http://adlnet.gov/expapi/verbs/experienced',
display: { 'en-US': 'experienced' }
});
const activity = new TinCan.Activity({
id: 'http://example.com/activities/tincanjs-test',
definition: {
name: { 'en-US': 'TinCanJS Test Activity' },
description: { 'en-US': 'A test activity for TinCanJS quickstart.' }
}
});
const statement = new TinCan.Statement({
actor: actor,
verb: verb,
object: activity
});
lrs.saveStatement(statement, function (err, xhr) {
if (err) {
console.error('Failed to save statement:', err);
return;
}
console.log('Statement saved successfully with ID:', JSON.parse(xhr.responseText)[0]);
});
// Example of retrieving statements
lrs.queryStatements({}, function (err, result) {
if (err) {
console.error('Failed to query statements:', err);
return;
}
console.log('Queried statements count:', result.statements.length);
});