@pie-qti/element-schemas 0.1.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.
Files changed (40) hide show
  1. package/.turbo/turbo-build.log +2 -0
  2. package/CHANGELOG.md +7 -0
  3. package/README.md +107 -0
  4. package/dist/index.d.ts +33 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +80 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/schemas/drag-in-the-blank.json +1181 -0
  9. package/dist/schemas/ebsr.json +2911 -0
  10. package/dist/schemas/explicit-constructed-response.json +1130 -0
  11. package/dist/schemas/extended-text-entry.json +954 -0
  12. package/dist/schemas/hotspot.json +1204 -0
  13. package/dist/schemas/image-cloze-association.json +1085 -0
  14. package/dist/schemas/inline-dropdown.json +1152 -0
  15. package/dist/schemas/match-list.json +170 -0
  16. package/dist/schemas/match.json +1530 -0
  17. package/dist/schemas/multiple-choice.json +1332 -0
  18. package/dist/schemas/passage.json +775 -0
  19. package/dist/schemas/placement-ordering.json +1613 -0
  20. package/dist/schemas/select-text.json +1248 -0
  21. package/package.json +34 -0
  22. package/schema-versions.json +80 -0
  23. package/scripts/check-schema-versions.ts +112 -0
  24. package/scripts/sync-schemas.ts +171 -0
  25. package/src/index.ts +94 -0
  26. package/src/schemas/drag-in-the-blank.json +1181 -0
  27. package/src/schemas/ebsr.json +2911 -0
  28. package/src/schemas/explicit-constructed-response.json +1130 -0
  29. package/src/schemas/extended-text-entry.json +954 -0
  30. package/src/schemas/hotspot.json +1204 -0
  31. package/src/schemas/image-cloze-association.json +1085 -0
  32. package/src/schemas/inline-dropdown.json +1152 -0
  33. package/src/schemas/match-list.json +170 -0
  34. package/src/schemas/match.json +1530 -0
  35. package/src/schemas/multiple-choice.json +1332 -0
  36. package/src/schemas/passage.json +775 -0
  37. package/src/schemas/placement-ordering.json +1613 -0
  38. package/src/schemas/select-text.json +1248 -0
  39. package/tsconfig.json +9 -0
  40. package/tsconfig.tsbuildinfo +1 -0
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@pie-qti/element-schemas",
3
+ "version": "0.1.1",
4
+ "description": "JSON schemas for PIE element validation",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "build": "tsc && bun run copy-schemas",
17
+ "copy-schemas": "mkdir -p dist/schemas && cp src/schemas/*.json dist/schemas/",
18
+ "clean": "rm -rf dist",
19
+ "sync": "bun run scripts/sync-schemas.ts",
20
+ "check-updates": "bun run scripts/check-schema-versions.ts",
21
+ "typecheck": "tsc --noEmit"
22
+ },
23
+ "keywords": [
24
+ "pie",
25
+ "schemas",
26
+ "validation",
27
+ "json-schema"
28
+ ],
29
+ "author": "Renaissance Learning",
30
+ "license": "ISC",
31
+ "devDependencies": {
32
+ "typescript": "^5.9.3"
33
+ }
34
+ }
@@ -0,0 +1,80 @@
1
+ {
2
+ "multiple-choice": {
3
+ "version": "11.4.3",
4
+ "schemaHash": "e7ead176068039d5",
5
+ "syncedAt": "2025-12-24T16:34:02.207Z",
6
+ "sourcePath": "/Users/eelco.hillenius/dev/prj/pie/pie-elements/packages/multiple-choice/docs/pie-schema.json"
7
+ },
8
+ "extended-text-entry": {
9
+ "version": "13.4.0",
10
+ "schemaHash": "0a22fb0f93b3f1ea",
11
+ "syncedAt": "2025-12-24T16:34:02.223Z",
12
+ "sourcePath": "/Users/eelco.hillenius/dev/prj/pie/pie-elements/packages/extended-text-entry/docs/pie-schema.json"
13
+ },
14
+ "placement-ordering": {
15
+ "version": "12.3.3",
16
+ "schemaHash": "0b5c0addd725dcde",
17
+ "syncedAt": "2025-12-24T17:09:04.941Z",
18
+ "sourcePath": "/Users/eelco.hillenius/dev/prj/pie/pie-elements/packages/placement-ordering/docs/pie-schema.json"
19
+ },
20
+ "match": {
21
+ "version": "10.3.3",
22
+ "schemaHash": "31b9998c4211e68c",
23
+ "syncedAt": "2025-12-24T17:21:48.119Z",
24
+ "sourcePath": "/Users/eelco.hillenius/dev/prj/pie/pie-elements/packages/match/docs/pie-schema.json"
25
+ },
26
+ "explicit-constructed-response": {
27
+ "version": "9.3.3",
28
+ "schemaHash": "a5243e3342e779de",
29
+ "syncedAt": "2025-12-24T17:23:54.274Z",
30
+ "sourcePath": "/Users/eelco.hillenius/dev/prj/pie/pie-elements/packages/explicit-constructed-response/docs/pie-schema.json"
31
+ },
32
+ "select-text": {
33
+ "version": "11.3.3",
34
+ "schemaHash": "6fa72bc0ae8b034c",
35
+ "syncedAt": "2025-12-24T17:33:19.489Z",
36
+ "sourcePath": "/Users/eelco.hillenius/dev/prj/pie/pie-elements/packages/select-text/docs/pie-schema.json"
37
+ },
38
+ "inline-dropdown": {
39
+ "version": "8.3.3",
40
+ "schemaHash": "05633d1a5f56813b",
41
+ "syncedAt": "2025-12-24T17:37:02.971Z",
42
+ "sourcePath": "/Users/eelco.hillenius/dev/prj/pie/pie-elements/packages/inline-dropdown/docs/pie-schema.json"
43
+ },
44
+ "drag-in-the-blank": {
45
+ "version": "8.3.3",
46
+ "schemaHash": "7e5e4f8d87b70510",
47
+ "syncedAt": "2025-12-24T18:40:10.126Z",
48
+ "sourcePath": "/Users/eelco.hillenius/dev/prj/pie/pie-elements/packages/drag-in-the-blank/docs/pie-schema.json"
49
+ },
50
+ "ebsr": {
51
+ "version": "12.3.3",
52
+ "schemaHash": "197c46f303507beb",
53
+ "syncedAt": "2025-12-24T18:53:19.334Z",
54
+ "sourcePath": "/Users/eelco.hillenius/dev/prj/pie/pie-elements/packages/ebsr/docs/pie-schema.json"
55
+ },
56
+ "hotspot": {
57
+ "version": "9.3.3",
58
+ "schemaHash": "cdbeab1ad844be95",
59
+ "syncedAt": "2025-12-24T19:44:36.088Z",
60
+ "sourcePath": "/Users/eelco.hillenius/dev/prj/pie/pie-elements/packages/hotspot/docs/pie-schema.json"
61
+ },
62
+ "image-cloze-association": {
63
+ "version": "8.3.3",
64
+ "schemaHash": "ade9a4ee89e5c3a8",
65
+ "syncedAt": "2025-12-24T19:51:00.860Z",
66
+ "sourcePath": "/Users/eelco.hillenius/dev/prj/pie/pie-elements/packages/image-cloze-association/docs/pie-schema.json"
67
+ },
68
+ "match-list": {
69
+ "version": "5.3.3",
70
+ "schemaHash": "da21e978152d1fb8",
71
+ "syncedAt": "2025-12-24T19:53:09.293Z",
72
+ "sourcePath": "/Users/eelco.hillenius/dev/prj/pie/pie-elements/packages/match-list/docs/pie-schema.json"
73
+ },
74
+ "passage": {
75
+ "version": "5.3.3",
76
+ "schemaHash": "b3a3989106088973",
77
+ "syncedAt": "2025-12-24T19:55:22.498Z",
78
+ "sourcePath": "/Users/eelco.hillenius/dev/prj/pie/pie-elements/packages/passage/docs/pie-schema.json"
79
+ }
80
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Schema Version Checker
3
+ *
4
+ * Checks if local schemas are up-to-date with pie-elements repository.
5
+ * Useful for CI to detect schema drift.
6
+ */
7
+
8
+ import { createHash } from 'node:crypto';
9
+ import { existsSync } from 'node:fs';
10
+ import { readFile } from 'node:fs/promises';
11
+ import { resolve } from 'node:path';
12
+
13
+ const PIE_ELEMENTS_PATH = resolve(import.meta.dirname, '../../../../pie-elements/packages');
14
+ const SCHEMAS_OUTPUT = resolve(import.meta.dirname, '../src/schemas');
15
+ const VERSIONS_FILE = resolve(import.meta.dirname, '../schema-versions.json');
16
+
17
+ interface SchemaVersion {
18
+ version: string;
19
+ schemaHash: string;
20
+ syncedAt: string;
21
+ sourcePath: string;
22
+ }
23
+
24
+ interface SchemaVersions {
25
+ [elementName: string]: SchemaVersion;
26
+ }
27
+
28
+ function calculateHash(content: string): string {
29
+ return createHash('sha256').update(content).digest('hex').substring(0, 16);
30
+ }
31
+
32
+ async function loadVersions(): Promise<SchemaVersions> {
33
+ if (!existsSync(VERSIONS_FILE)) {
34
+ console.warn('⚠️ No schema-versions.json found. Run `bun run sync` first.');
35
+ return {};
36
+ }
37
+ const content = await readFile(VERSIONS_FILE, 'utf-8');
38
+ return JSON.parse(content);
39
+ }
40
+
41
+ async function checkSchemas() {
42
+ console.log('🔍 Checking schema versions...\n');
43
+
44
+ if (!existsSync(PIE_ELEMENTS_PATH)) {
45
+ console.error(`❌ PIE elements repository not found at: ${PIE_ELEMENTS_PATH}`);
46
+ process.exit(1);
47
+ }
48
+
49
+ const versions = await loadVersions();
50
+ const elements = Object.keys(versions);
51
+
52
+ if (elements.length === 0) {
53
+ console.error('❌ No schemas tracked. Run `bun run sync` first.');
54
+ process.exit(1);
55
+ }
56
+
57
+ let outdatedCount = 0;
58
+ let upToDateCount = 0;
59
+ let missingCount = 0;
60
+
61
+ for (const element of elements) {
62
+ const info = versions[element];
63
+ const schemaPath = resolve(PIE_ELEMENTS_PATH, element, 'docs/pie-schema.json');
64
+ const localPath = resolve(SCHEMAS_OUTPUT, `${element}.json`);
65
+
66
+ // Check if local schema exists
67
+ if (!existsSync(localPath)) {
68
+ console.error(`❌ ${element}: Local schema missing at ${localPath}`);
69
+ missingCount++;
70
+ continue;
71
+ }
72
+
73
+ // Check if source schema exists
74
+ if (!existsSync(schemaPath)) {
75
+ console.warn(`⚠️ ${element}: Source schema not found at ${schemaPath}`);
76
+ continue;
77
+ }
78
+
79
+ try {
80
+ // Check if source has changed
81
+ const sourceContent = await readFile(schemaPath, 'utf-8');
82
+ const sourceHash = calculateHash(sourceContent);
83
+
84
+ if (sourceHash !== info.schemaHash) {
85
+ console.warn(`⚠️ ${element}: Schema out of date (local: ${info.schemaHash}, remote: ${sourceHash})`);
86
+ outdatedCount++;
87
+ } else {
88
+ console.log(`✅ ${element}: Up-to-date (v${info.version})`);
89
+ upToDateCount++;
90
+ }
91
+ } catch (error) {
92
+ console.error(`❌ ${element}: Error checking schema - ${(error as Error).message}`);
93
+ }
94
+ }
95
+
96
+ console.log('\n📊 Status:');
97
+ console.log(` ✅ Up-to-date: ${upToDateCount}`);
98
+ console.log(` ⚠️ Outdated: ${outdatedCount}`);
99
+ console.log(` ❌ Missing: ${missingCount}`);
100
+
101
+ if (outdatedCount > 0 || missingCount > 0) {
102
+ console.log('\n💡 Run `bun run sync` to update schemas.');
103
+ process.exit(1);
104
+ }
105
+
106
+ console.log('\n✨ All schemas are up-to-date!');
107
+ }
108
+
109
+ checkSchemas().catch((error) => {
110
+ console.error('Fatal error:', error);
111
+ process.exit(1);
112
+ });
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Schema Sync Script
3
+ *
4
+ * Syncs PIE element schemas from the pie-elements repository.
5
+ * Assumes pie-elements is checked out as a sibling directory.
6
+ */
7
+
8
+ import { createHash } from 'node:crypto';
9
+ import { existsSync } from 'node:fs';
10
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
11
+ import { resolve } from 'node:path';
12
+
13
+ // Relative path to pie-elements repository
14
+ const PIE_ELEMENTS_PATH = resolve(import.meta.dirname, '../../../../pie-elements/packages');
15
+ const SCHEMAS_OUTPUT = resolve(import.meta.dirname, '../src/schemas');
16
+ const VERSIONS_FILE = resolve(import.meta.dirname, '../schema-versions.json');
17
+
18
+ // Elements to sync schemas for
19
+ const ELEMENTS_TO_SYNC = [
20
+ 'multiple-choice',
21
+ 'extended-text-entry',
22
+ 'placement-ordering',
23
+ 'match',
24
+ 'explicit-constructed-response',
25
+ 'select-text',
26
+ 'inline-dropdown',
27
+ 'drag-in-the-blank',
28
+ 'ebsr',
29
+ 'hotspot',
30
+ 'image-cloze-association',
31
+ 'match-list',
32
+ 'passage',
33
+ // Add more elements as needed
34
+ ];
35
+ interface SchemaVersion {
36
+ version: string;
37
+ schemaHash: string;
38
+ syncedAt: string;
39
+ sourcePath: string;
40
+ }
41
+
42
+ interface SchemaVersions {
43
+ [elementName: string]: SchemaVersion;
44
+ }
45
+
46
+ /**
47
+ * Calculate SHA-256 hash of schema content
48
+ */
49
+ function calculateHash(content: string): string {
50
+ return createHash('sha256').update(content).digest('hex').substring(0, 16);
51
+ }
52
+
53
+ /**
54
+ * Load existing schema versions
55
+ */
56
+ async function loadVersions(): Promise<SchemaVersions> {
57
+ if (!existsSync(VERSIONS_FILE)) {
58
+ return {};
59
+ }
60
+ const content = await readFile(VERSIONS_FILE, 'utf-8');
61
+ return JSON.parse(content);
62
+ }
63
+
64
+ /**
65
+ * Save schema versions
66
+ */
67
+ async function saveVersions(versions: SchemaVersions): Promise<void> {
68
+ const content = JSON.stringify(versions, null, 2);
69
+ await writeFile(VERSIONS_FILE, content, 'utf-8');
70
+ }
71
+
72
+ /**
73
+ * Get package version from package.json
74
+ */
75
+ async function getPackageVersion(elementPath: string): Promise<string> {
76
+ try {
77
+ const pkgPath = resolve(elementPath, 'package.json');
78
+ const pkgContent = await readFile(pkgPath, 'utf-8');
79
+ const pkg = JSON.parse(pkgContent);
80
+ return pkg.version || 'unknown';
81
+ } catch {
82
+ return 'unknown';
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Sync schemas from pie-elements
88
+ */
89
+ async function syncSchemas() {
90
+ console.log('🔄 Syncing PIE element schemas...\n');
91
+
92
+ // Check if pie-elements exists
93
+ if (!existsSync(PIE_ELEMENTS_PATH)) {
94
+ console.error(`❌ PIE elements repository not found at: ${PIE_ELEMENTS_PATH}`);
95
+ console.error(' Please ensure pie-elements is checked out as a sibling directory.');
96
+ process.exit(1);
97
+ }
98
+
99
+ // Load existing versions
100
+ const versions = await loadVersions();
101
+ const updatedVersions: SchemaVersions = {};
102
+ let syncCount = 0;
103
+ let skipCount = 0;
104
+ let errorCount = 0;
105
+
106
+ // Create output directory
107
+ await mkdir(SCHEMAS_OUTPUT, { recursive: true });
108
+
109
+ for (const element of ELEMENTS_TO_SYNC) {
110
+ const elementPath = resolve(PIE_ELEMENTS_PATH, element);
111
+ const schemaPath = resolve(elementPath, 'docs/pie-schema.json');
112
+ const outputPath = resolve(SCHEMAS_OUTPUT, `${element}.json`);
113
+
114
+ try {
115
+ // Check if schema exists
116
+ if (!existsSync(schemaPath)) {
117
+ console.warn(`⚠️ ${element}: Schema not found at ${schemaPath}`);
118
+ errorCount++;
119
+ continue;
120
+ }
121
+
122
+ // Read schema
123
+ const schemaContent = await readFile(schemaPath, 'utf-8');
124
+ const schemaHash = calculateHash(schemaContent);
125
+ const version = await getPackageVersion(elementPath);
126
+
127
+ // Check if schema has changed
128
+ const existing = versions[element];
129
+ if (existing && existing.schemaHash === schemaHash) {
130
+ console.log(`⏭️ ${element}: Already up-to-date (v${version})`);
131
+ updatedVersions[element] = existing;
132
+ skipCount++;
133
+ continue;
134
+ }
135
+
136
+ // Write schema
137
+ await writeFile(outputPath, schemaContent, 'utf-8');
138
+
139
+ // Update version info
140
+ updatedVersions[element] = {
141
+ version,
142
+ schemaHash,
143
+ syncedAt: new Date().toISOString(),
144
+ sourcePath: schemaPath,
145
+ };
146
+
147
+ console.log(`✅ ${element}: Synced v${version} (hash: ${schemaHash})`);
148
+ syncCount++;
149
+
150
+ } catch (error) {
151
+ console.error(`❌ ${element}: ${(error as Error).message}`);
152
+ errorCount++;
153
+ }
154
+ }
155
+
156
+ // Save version metadata
157
+ await saveVersions(updatedVersions);
158
+
159
+ // Summary
160
+ console.log('\n📊 Sync Summary:');
161
+ console.log(` ✅ Synced: ${syncCount}`);
162
+ console.log(` ⏭️ Skipped (up-to-date): ${skipCount}`);
163
+ console.log(` ❌ Errors: ${errorCount}`);
164
+ console.log(`\n✨ Schema versions saved to: ${VERSIONS_FILE}`);
165
+ }
166
+
167
+ // Run sync
168
+ syncSchemas().catch((error) => {
169
+ console.error('Fatal error:', error);
170
+ process.exit(1);
171
+ });
package/src/index.ts ADDED
@@ -0,0 +1,94 @@
1
+ /**
2
+ * @pie-qti/element-schemas
3
+ *
4
+ * PIE element JSON schemas for validation
5
+ */
6
+
7
+ import { readFile } from 'node:fs/promises';
8
+ import { dirname, resolve } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+
13
+ /**
14
+ * Available PIE element schemas
15
+ */
16
+ export const AVAILABLE_SCHEMAS = [
17
+ 'multiple-choice',
18
+ 'extended-text-entry',
19
+ 'placement-ordering',
20
+ 'match',
21
+ 'explicit-constructed-response',
22
+ 'select-text',
23
+ 'inline-dropdown',
24
+ 'drag-in-the-blank',
25
+ 'ebsr',
26
+ 'hotspot',
27
+ 'image-cloze-association',
28
+ 'match-list',
29
+ 'passage',
30
+ ] as const;
31
+
32
+ export type ElementType = typeof AVAILABLE_SCHEMAS[number];
33
+
34
+ /**
35
+ * Schema cache
36
+ */
37
+ const schemaCache = new Map<string, object>();
38
+
39
+ /**
40
+ * Load a PIE element schema
41
+ * @param elementType - Element type (e.g., 'multiple-choice')
42
+ * @returns JSON schema object
43
+ */
44
+ export async function loadSchema(elementType: ElementType): Promise<object> {
45
+ // Check cache first
46
+ if (schemaCache.has(elementType)) {
47
+ return schemaCache.get(elementType)!;
48
+ }
49
+
50
+ const schemaPath = resolve(__dirname, `schemas/${elementType}.json`);
51
+
52
+ try {
53
+ const content = await readFile(schemaPath, 'utf-8');
54
+ const schema = JSON.parse(content);
55
+ schemaCache.set(elementType, schema);
56
+ return schema;
57
+ } catch (error) {
58
+ throw new Error(
59
+ `Failed to load schema for ${elementType}: ${(error as Error).message}`
60
+ );
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Load all available schemas
66
+ * @returns Map of element type to schema
67
+ */
68
+ export async function loadAllSchemas(): Promise<Map<string, object>> {
69
+ const schemas = new Map<string, object>();
70
+
71
+ for (const elementType of AVAILABLE_SCHEMAS) {
72
+ const schema = await loadSchema(elementType);
73
+ schemas.set(`@pie-element/${elementType}`, schema);
74
+ }
75
+
76
+ return schemas;
77
+ }
78
+
79
+ /**
80
+ * Get schema synchronously (must be loaded first)
81
+ * @param elementType - Element type
82
+ * @returns Cached schema or undefined
83
+ */
84
+ export function getSchema(elementType: ElementType): object | undefined {
85
+ return schemaCache.get(elementType);
86
+ }
87
+
88
+ /**
89
+ * Check if a schema is available
90
+ * @param elementType - Element type to check
91
+ */
92
+ export function hasSchema(elementType: string): elementType is ElementType {
93
+ return AVAILABLE_SCHEMAS.includes(elementType as ElementType);
94
+ }