openlearn-next 0.2.1 → 0.2.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/README.md +2 -0
- package/dist/assets/{courseware-BfnpYR-P.js → courseware-Cc-Yy0Ha.js} +1 -1
- package/dist/assets/index-CsN5CIqI.css +1 -0
- package/dist/assets/{index-zi7zNIte.js → index-D_zAVH7u.js} +101 -101
- package/dist/assets/{index.es-S9HBYdjC.js → index.es-CF5dm42F.js} +1 -1
- package/dist/assets/{whiteboard-BpCj1CQU.js → whiteboard-jgQrAUm7.js} +45 -14
- package/dist/index.html +2 -2
- package/dist/server.cjs +180 -34
- package/package.json +1 -1
- package/dist/assets/index-CaJDJGky.css +0 -1
package/dist/index.html
CHANGED
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
</script>
|
|
18
|
-
<script type="module" crossorigin src="/assets/index-
|
|
18
|
+
<script type="module" crossorigin src="/assets/index-D_zAVH7u.js"></script>
|
|
19
19
|
<link rel="stylesheet" crossorigin href="/assets/vendor-reveal-DcQQzuI9.css">
|
|
20
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
20
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CsN5CIqI.css">
|
|
21
21
|
</head>
|
|
22
22
|
<body>
|
|
23
23
|
<div id="root"></div>
|
package/dist/server.cjs
CHANGED
|
@@ -18418,9 +18418,69 @@ var BRIDGE_SDK_CODE = `(function() {
|
|
|
18418
18418
|
} catch (err) {}
|
|
18419
18419
|
}
|
|
18420
18420
|
|
|
18421
|
+
// \u2500\u2500 Helper: create a safe postMessage wrapper that normalises 'null' \u2192 '*' \u2500\u2500
|
|
18422
|
+
function __makeSafePostMessage(realTarget, label) {
|
|
18423
|
+
return function(message, targetOrigin, transfer) {
|
|
18424
|
+
try {
|
|
18425
|
+
if (message && typeof message === 'object') {
|
|
18426
|
+
if (!message.attempt_id && window.__LMS_STUDENT__?.attempt_id) {
|
|
18427
|
+
message.attempt_id = window.__LMS_STUDENT__.attempt_id;
|
|
18428
|
+
}
|
|
18429
|
+
if (!message.uuid && window.__LMS_COURSEWARE__?.uuid) {
|
|
18430
|
+
message.uuid = window.__LMS_COURSEWARE__.uuid;
|
|
18431
|
+
}
|
|
18432
|
+
}
|
|
18433
|
+
} catch (e) {}
|
|
18434
|
+
|
|
18435
|
+
var origin = targetOrigin;
|
|
18436
|
+
if (origin === 'null' || origin === null || origin === undefined) {
|
|
18437
|
+
console.warn('[LMS Bridge Notice] Normalized invalid targetOrigin "' + origin + '" to "*" for ' + label);
|
|
18438
|
+
origin = '*';
|
|
18439
|
+
}
|
|
18440
|
+
try {
|
|
18441
|
+
return realTarget.postMessage.call(realTarget, message, origin, transfer);
|
|
18442
|
+
} catch (err) {
|
|
18443
|
+
if (err.name === 'SyntaxError' && origin !== '*') {
|
|
18444
|
+
console.warn('[LMS Bridge Notice] Recovered SyntaxError on ' + label + ', fallback to "*"', err);
|
|
18445
|
+
return realTarget.postMessage.call(realTarget, message, '*', transfer);
|
|
18446
|
+
}
|
|
18447
|
+
throw err;
|
|
18448
|
+
}
|
|
18449
|
+
};
|
|
18450
|
+
}
|
|
18451
|
+
|
|
18452
|
+
// \u2500\u2500 Helper: create a Proxy wrapper around a cross-origin WindowProxy \u2500\u2500
|
|
18453
|
+
// This is necessary because sandboxed iframes (without allow-same-origin)
|
|
18454
|
+
// cannot set properties on cross-origin WindowProxy objects.
|
|
18455
|
+
// We use Object.defineProperty to shadow window.parent / window.top
|
|
18456
|
+
// with a Proxy that intercepts the .postMessage() call.
|
|
18457
|
+
function __proxyWindow(realRef, propName) {
|
|
18458
|
+
try {
|
|
18459
|
+
var safePost = __makeSafePostMessage(realRef, propName + '.postMessage');
|
|
18460
|
+
var proxyObj = new Proxy(realRef, {
|
|
18461
|
+
get: function(target, prop) {
|
|
18462
|
+
if (prop === 'postMessage') return safePost;
|
|
18463
|
+
try {
|
|
18464
|
+
var val = target[prop];
|
|
18465
|
+
if (typeof val === 'function') return val.bind(target);
|
|
18466
|
+
return val;
|
|
18467
|
+
} catch (e) { return undefined; }
|
|
18468
|
+
}
|
|
18469
|
+
});
|
|
18470
|
+
Object.defineProperty(window, propName, {
|
|
18471
|
+
get: function() { return proxyObj; },
|
|
18472
|
+
configurable: true
|
|
18473
|
+
});
|
|
18474
|
+
} catch (e) {
|
|
18475
|
+
// Proxy or defineProperty not supported, fall back to direct override attempt
|
|
18476
|
+
try { realRef.postMessage = __makeSafePostMessage(realRef, propName + '.postMessage (fallback)'); } catch (_) {}
|
|
18477
|
+
}
|
|
18478
|
+
}
|
|
18479
|
+
|
|
18421
18480
|
// Proxy postMessage calls to enrich them with attempt_id/uuid and normalize targetOrigin
|
|
18422
18481
|
try {
|
|
18423
|
-
|
|
18482
|
+
// 1. Override window.postMessage (self-targeting, always works)
|
|
18483
|
+
var originalPostMessage = window.postMessage;
|
|
18424
18484
|
window.postMessage = function(message, targetOrigin, transfer) {
|
|
18425
18485
|
try {
|
|
18426
18486
|
if (message && typeof message === 'object') {
|
|
@@ -18433,49 +18493,67 @@ var BRIDGE_SDK_CODE = `(function() {
|
|
|
18433
18493
|
}
|
|
18434
18494
|
} catch (e) {}
|
|
18435
18495
|
|
|
18436
|
-
|
|
18437
|
-
if (origin === 'null') {
|
|
18496
|
+
var origin = targetOrigin;
|
|
18497
|
+
if (origin === 'null' || origin === null || origin === undefined) {
|
|
18498
|
+
console.warn('[LMS Bridge Notice] Normalized invalid targetOrigin "null" to "*" for postMessage call');
|
|
18438
18499
|
origin = '*';
|
|
18439
18500
|
}
|
|
18440
18501
|
try {
|
|
18441
18502
|
return originalPostMessage.call(this, message, origin, transfer);
|
|
18442
18503
|
} catch (err) {
|
|
18443
18504
|
if (err.name === 'SyntaxError' && origin !== '*') {
|
|
18505
|
+
console.warn('[LMS Bridge Notice] Recovered SyntaxError on postMessage, fallback targetOrigin to "*"', err);
|
|
18444
18506
|
return originalPostMessage.call(this, message, '*', transfer);
|
|
18445
18507
|
}
|
|
18446
18508
|
throw err;
|
|
18447
18509
|
}
|
|
18448
18510
|
};
|
|
18449
18511
|
|
|
18512
|
+
// 2. Shadow window.parent with a Proxy that intercepts .postMessage()
|
|
18450
18513
|
if (window.parent && window.parent !== window) {
|
|
18451
|
-
|
|
18452
|
-
|
|
18453
|
-
|
|
18454
|
-
|
|
18455
|
-
|
|
18456
|
-
|
|
18457
|
-
|
|
18458
|
-
|
|
18459
|
-
|
|
18460
|
-
|
|
18514
|
+
__proxyWindow(window.parent, 'parent');
|
|
18515
|
+
}
|
|
18516
|
+
|
|
18517
|
+
// 3. Shadow window.top with a Proxy that intercepts .postMessage()
|
|
18518
|
+
try {
|
|
18519
|
+
if (window.top && window.top !== window) {
|
|
18520
|
+
__proxyWindow(window.top, 'top');
|
|
18521
|
+
}
|
|
18522
|
+
} catch (e) {}
|
|
18523
|
+
|
|
18524
|
+
// 4. Intercept message event listeners to sanitize event.source.postMessage replies
|
|
18525
|
+
var origAddEventListener = window.addEventListener;
|
|
18526
|
+
if (typeof origAddEventListener === 'function') {
|
|
18527
|
+
window.addEventListener = function(type, listener, options) {
|
|
18528
|
+
if (type === 'message' && typeof listener === 'function') {
|
|
18529
|
+
var wrappedListener = function(event) {
|
|
18530
|
+
try {
|
|
18531
|
+
if (event && event.source && typeof event.source.postMessage === 'function') {
|
|
18532
|
+
var origSourcePostMessage = event.source.postMessage;
|
|
18533
|
+
event.source.postMessage = function(msg, targetOrigin, transfer) {
|
|
18534
|
+
var origin = targetOrigin;
|
|
18535
|
+
if (origin === 'null' || origin === null) {
|
|
18536
|
+
console.warn('[LMS Bridge Notice] Normalized invalid targetOrigin "null" to "*" on event.source.postMessage');
|
|
18537
|
+
origin = '*';
|
|
18538
|
+
}
|
|
18539
|
+
try {
|
|
18540
|
+
return origSourcePostMessage.call(event.source, msg, origin, transfer);
|
|
18541
|
+
} catch (err) {
|
|
18542
|
+
if (err.name === 'SyntaxError' && origin !== '*') {
|
|
18543
|
+
console.warn('[LMS Bridge Notice] Recovered SyntaxError on event.source.postMessage, fallback to "*"', err);
|
|
18544
|
+
return origSourcePostMessage.call(event.source, msg, '*', transfer);
|
|
18545
|
+
}
|
|
18546
|
+
throw err;
|
|
18547
|
+
}
|
|
18548
|
+
};
|
|
18461
18549
|
}
|
|
18462
|
-
}
|
|
18463
|
-
|
|
18464
|
-
|
|
18465
|
-
|
|
18466
|
-
|
|
18467
|
-
|
|
18468
|
-
|
|
18469
|
-
try {
|
|
18470
|
-
return parentPostMessage.call(window.parent, message, origin, transfer);
|
|
18471
|
-
} catch (err) {
|
|
18472
|
-
if (err.name === 'SyntaxError' && origin !== '*') {
|
|
18473
|
-
return parentPostMessage.call(window.parent, message, '*', transfer);
|
|
18474
|
-
}
|
|
18475
|
-
throw err;
|
|
18476
|
-
}
|
|
18477
|
-
};
|
|
18478
|
-
} catch (e) {}
|
|
18550
|
+
} catch (e) {}
|
|
18551
|
+
return listener.apply(this, arguments);
|
|
18552
|
+
};
|
|
18553
|
+
return origAddEventListener.call(this, type, wrappedListener, options);
|
|
18554
|
+
}
|
|
18555
|
+
return origAddEventListener.apply(this, arguments);
|
|
18556
|
+
};
|
|
18479
18557
|
}
|
|
18480
18558
|
} catch (e) {}
|
|
18481
18559
|
|
|
@@ -20001,7 +20079,8 @@ function registerBridgeRoutes(ctx) {
|
|
|
20001
20079
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
20002
20080
|
res.send(BRIDGE_SDK_CODE);
|
|
20003
20081
|
});
|
|
20004
|
-
app.get("/runtime/:uuid", (req, res) => {
|
|
20082
|
+
app.get("/runtime/:uuid", (req, res, next) => {
|
|
20083
|
+
if (req.path.endsWith("/")) return next();
|
|
20005
20084
|
res.redirect(`/runtime/${req.params.uuid}/`);
|
|
20006
20085
|
});
|
|
20007
20086
|
app.get("/runtime/:uuid/*", (req, res) => {
|
|
@@ -20061,6 +20140,7 @@ function registerBridgeRoutes(ctx) {
|
|
|
20061
20140
|
|
|
20062
20141
|
// server/routes/lessons.ts
|
|
20063
20142
|
var import_genai3 = require("@google/genai");
|
|
20143
|
+
var import_uuid16 = require("uuid");
|
|
20064
20144
|
function registerLessonsRoutes(ctx) {
|
|
20065
20145
|
const {
|
|
20066
20146
|
app,
|
|
@@ -20470,6 +20550,72 @@ Provide a short, friendly, and helpful hint (1-2 sentences) directly related to
|
|
|
20470
20550
|
res.status(500).json({ error: e.message });
|
|
20471
20551
|
}
|
|
20472
20552
|
});
|
|
20553
|
+
app.delete("/api/lessons/:id", async (req, res) => {
|
|
20554
|
+
try {
|
|
20555
|
+
const { id } = req.params;
|
|
20556
|
+
kernelContainer.db.prepare("DELETE FROM whiteboard_elements WHERE lesson_id = ?").run(id);
|
|
20557
|
+
kernelContainer.db.prepare("DELETE FROM student_lesson_progress WHERE lesson_id = ?").run(id);
|
|
20558
|
+
kernelContainer.db.prepare("DELETE FROM schedules WHERE lesson_id = ?").run(id);
|
|
20559
|
+
kernelContainer.db.prepare("DELETE FROM assignments WHERE lesson_id = ?").run(id);
|
|
20560
|
+
const result = kernelContainer.db.prepare("DELETE FROM lessons WHERE id = ?").run(id);
|
|
20561
|
+
if (result.changes === 0) {
|
|
20562
|
+
return res.status(404).json({ error: "Lesson not found" });
|
|
20563
|
+
}
|
|
20564
|
+
res.json({ success: true });
|
|
20565
|
+
} catch (e) {
|
|
20566
|
+
res.status(500).json({ error: e.message });
|
|
20567
|
+
}
|
|
20568
|
+
});
|
|
20569
|
+
app.get("/api/lessons/:id/stats", (req, res) => {
|
|
20570
|
+
try {
|
|
20571
|
+
const { id } = req.params;
|
|
20572
|
+
const whiteboardCount = kernelContainer.db.prepare(
|
|
20573
|
+
"SELECT COUNT(*) as count FROM whiteboard_elements WHERE lesson_id = ?"
|
|
20574
|
+
).get(id).count;
|
|
20575
|
+
const scheduleCount = kernelContainer.db.prepare(
|
|
20576
|
+
"SELECT COUNT(*) as count FROM schedules WHERE lesson_id = ?"
|
|
20577
|
+
).get(id).count;
|
|
20578
|
+
const enrollmentCount = kernelContainer.db.prepare(
|
|
20579
|
+
"SELECT COUNT(*) as count FROM student_lesson_progress WHERE lesson_id = ?"
|
|
20580
|
+
).get(id).count;
|
|
20581
|
+
const assignmentCount = kernelContainer.db.prepare(
|
|
20582
|
+
"SELECT COUNT(*) as count FROM assignments WHERE lesson_id = ?"
|
|
20583
|
+
).get(id).count;
|
|
20584
|
+
res.json({ whiteboardCount, scheduleCount, enrollmentCount, assignmentCount });
|
|
20585
|
+
} catch (e) {
|
|
20586
|
+
res.status(500).json({ error: e.message });
|
|
20587
|
+
}
|
|
20588
|
+
});
|
|
20589
|
+
app.post("/api/lessons/:id/clone", async (req, res) => {
|
|
20590
|
+
try {
|
|
20591
|
+
const { id } = req.params;
|
|
20592
|
+
const original = kernelContainer.db.prepare("SELECT * FROM lessons WHERE id = ?").get(id);
|
|
20593
|
+
if (!original) {
|
|
20594
|
+
return res.status(404).json({ error: "Lesson not found" });
|
|
20595
|
+
}
|
|
20596
|
+
const newId = (0, import_uuid16.v7)();
|
|
20597
|
+
const now = Date.now();
|
|
20598
|
+
const newTitle = `\u526F\u672C-${original.title}`;
|
|
20599
|
+
kernelContainer.db.prepare(
|
|
20600
|
+
"INSERT INTO lessons (id, title, content, timeline, progress_mode, progress_conditions, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
|
|
20601
|
+
).run(newId, newTitle, original.content, original.timeline, original.progress_mode, original.progress_conditions, now, now);
|
|
20602
|
+
const whiteboardElements = kernelContainer.db.prepare(
|
|
20603
|
+
"SELECT * FROM whiteboard_elements WHERE lesson_id = ?"
|
|
20604
|
+
).all(id);
|
|
20605
|
+
const insertElement = kernelContainer.db.prepare(
|
|
20606
|
+
"INSERT INTO whiteboard_elements (id, lesson_id, type, data, created_at) VALUES (?, ?, ?, ?, ?)"
|
|
20607
|
+
);
|
|
20608
|
+
for (const el of whiteboardElements) {
|
|
20609
|
+
insertElement.run((0, import_uuid16.v7)(), newId, el.type, el.data, now);
|
|
20610
|
+
}
|
|
20611
|
+
const cloned = kernelContainer.db.prepare(
|
|
20612
|
+
"SELECT l.*, 0 as enrollment_count FROM lessons l WHERE l.id = ?"
|
|
20613
|
+
).get(newId);
|
|
20614
|
+
res.json({ success: true, lesson: cloned });
|
|
20615
|
+
} catch (e) {
|
|
20616
|
+
res.status(500).json({ error: e.message });
|
|
20617
|
+
}
|
|
20618
|
+
});
|
|
20473
20619
|
}
|
|
20474
20620
|
|
|
20475
20621
|
// server/routes/workspace.ts
|
|
@@ -22178,9 +22324,9 @@ Provide a grade score (0-100) and brief feedback. Ensure you output in this exac
|
|
|
22178
22324
|
} catch (e) {
|
|
22179
22325
|
}
|
|
22180
22326
|
}
|
|
22181
|
-
const { v7:
|
|
22327
|
+
const { v7: uuidv717 } = await import("uuid");
|
|
22182
22328
|
await kernelContainer.commandBus.execute({
|
|
22183
|
-
id:
|
|
22329
|
+
id: uuidv717(),
|
|
22184
22330
|
type: "ai.apply_grade",
|
|
22185
22331
|
actorId: "system",
|
|
22186
22332
|
timestamp: Date.now(),
|
|
@@ -23877,7 +24023,7 @@ async function startServer() {
|
|
|
23877
24023
|
imgSrc: ["'self'", "data:", "blob:", "https:"],
|
|
23878
24024
|
connectSrc: ["'self'", "ws:", "wss:", "https:"],
|
|
23879
24025
|
fontSrc: ["'self'", "data:", "https:", "https://fonts.gstatic.com"],
|
|
23880
|
-
frameSrc: ["'self'"],
|
|
24026
|
+
frameSrc: ["'self'", "blob:", "data:", "http://localhost", "http://127.0.0.1", "http:", "https:"],
|
|
23881
24027
|
objectSrc: ["'none'"]
|
|
23882
24028
|
}
|
|
23883
24029
|
},
|