@plasius/learning 0.2.19 → 0.2.20

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 CHANGED
@@ -54,6 +54,30 @@ const result = calculateAssessment(rubric, [
54
54
  console.log(result.score, result.completed);
55
55
  ```
56
56
 
57
+ ## Validate a Guardian-approved static project
58
+
59
+ The publishing contracts cover only immutable evidence and safe render models.
60
+ HTTP, persistence, identity checks, scanning implementations and hosting remain
61
+ consumer responsibilities. A public renderer must consume the allow-listed
62
+ `renderModel`; it must never execute learner source.
63
+
64
+ ```ts
65
+ import {
66
+ assertValidStaticProjectGuardianApproval,
67
+ assertValidStaticProjectPublication,
68
+ assertValidStaticProjectSnapshot,
69
+ } from "@plasius/learning";
70
+
71
+ assertValidStaticProjectSnapshot(snapshot);
72
+ assertValidStaticProjectGuardianApproval(approval);
73
+ assertValidStaticProjectPublication(publication);
74
+ ```
75
+
76
+ The version-one contract supports the three launch web projects, requires a
77
+ score of at least 80 plus mandatory safety evidence, records all eight scanner
78
+ checks, binds adult approval to the exact snapshot digest, and requires an
79
+ unlisted HTTPS URL with a random slug and explicit expiry.
80
+
57
81
  ## Validate mission authoring
58
82
 
59
83
  Mission authoring is additive to the immutable catalog. A bundle contains
package/dist/index.cjs CHANGED
@@ -42,15 +42,27 @@ __export(index_exports, {
42
42
  SERVO_CREATURE_MISSION_ONE_AUTHORING_V1: () => SERVO_CREATURE_MISSION_ONE_AUTHORING_V1,
43
43
  SKYWING_SPRINT_MISSION_ONE_AUTHORING_V1: () => SKYWING_SPRINT_MISSION_ONE_AUTHORING_V1,
44
44
  STAR_DEFENDER_SQUADRON_MISSION_ONE_AUTHORING_V1: () => STAR_DEFENDER_SQUADRON_MISSION_ONE_AUTHORING_V1,
45
+ STATIC_PROJECT_APPROVAL_STATEMENT_VERSION_V1: () => STATIC_PROJECT_APPROVAL_STATEMENT_VERSION_V1,
46
+ STATIC_PROJECT_DEFAULT_LIFETIME_DAYS_V1: () => STATIC_PROJECT_DEFAULT_LIFETIME_DAYS_V1,
47
+ STATIC_PROJECT_MAX_SOURCE_CHARACTERS_V1: () => STATIC_PROJECT_MAX_SOURCE_CHARACTERS_V1,
48
+ STATIC_PROJECT_PUBLISHING_CONTRACT_VERSION_V1: () => STATIC_PROJECT_PUBLISHING_CONTRACT_VERSION_V1,
49
+ STATIC_PROJECT_SAFETY_CHECK_IDS_V1: () => STATIC_PROJECT_SAFETY_CHECK_IDS_V1,
50
+ STATIC_PROJECT_SCANNER_VERSION_V1: () => STATIC_PROJECT_SCANNER_VERSION_V1,
45
51
  VIBE_BUG_DETECTIVE_MISSION_ONE_AUTHORING_V1: () => VIBE_BUG_DETECTIVE_MISSION_ONE_AUTHORING_V1,
46
52
  VIBE_GAME_REMIX_LAB_MISSION_ONE_AUTHORING_V1: () => VIBE_GAME_REMIX_LAB_MISSION_ONE_AUTHORING_V1,
47
53
  VIBE_IDEA_STUDIO_MISSION_ONE_AUTHORING_V1: () => VIBE_IDEA_STUDIO_MISSION_ONE_AUTHORING_V1,
48
54
  assertValidLearningPath: () => assertValidLearningPath,
49
55
  assertValidMissionAuthoringBundle: () => assertValidMissionAuthoringBundle,
56
+ assertValidStaticProjectGuardianApproval: () => assertValidStaticProjectGuardianApproval,
57
+ assertValidStaticProjectPublication: () => assertValidStaticProjectPublication,
58
+ assertValidStaticProjectSnapshot: () => assertValidStaticProjectSnapshot,
50
59
  calculateAssessment: () => calculateAssessment,
51
60
  validateAssessmentRubric: () => validateAssessmentRubric,
52
61
  validateLearningPath: () => validateLearningPath,
53
- validateMissionAuthoringBundle: () => validateMissionAuthoringBundle
62
+ validateMissionAuthoringBundle: () => validateMissionAuthoringBundle,
63
+ validateStaticProjectGuardianApproval: () => validateStaticProjectGuardianApproval,
64
+ validateStaticProjectPublication: () => validateStaticProjectPublication,
65
+ validateStaticProjectSnapshot: () => validateStaticProjectSnapshot
54
66
  });
55
67
  module.exports = __toCommonJS(index_exports);
56
68
 
@@ -627,6 +639,135 @@ var JUNIOR_CODER_ROBOT_RESCUE_PATH_CURRENT = JUNIOR_CODER_ROBOT_RESCUE_PATH_V1_1
627
639
  // src/contracts.ts
628
640
  var MISSION_AUTHORING_CONTRACT_VERSION_V1 = "1.0.0";
629
641
 
642
+ // src/publishing.ts
643
+ var STATIC_PROJECT_PUBLISHING_CONTRACT_VERSION_V1 = "1.0.0";
644
+ var STATIC_PROJECT_SCANNER_VERSION_V1 = "junior-coder-static-scan-v1";
645
+ var STATIC_PROJECT_APPROVAL_STATEMENT_VERSION_V1 = "guardian-static-publication-v1";
646
+ var STATIC_PROJECT_DEFAULT_LIFETIME_DAYS_V1 = 90;
647
+ var STATIC_PROJECT_MAX_SOURCE_CHARACTERS_V1 = 8e3;
648
+ var STATIC_PROJECT_SAFETY_CHECK_IDS_V1 = [
649
+ "allow-listed-source",
650
+ "no-personal-details",
651
+ "no-external-network",
652
+ "no-executable-markup",
653
+ "no-transmitting-forms",
654
+ "no-uploads-or-embeds",
655
+ "no-trackers-or-advertising",
656
+ "no-account-identifiers"
657
+ ];
658
+ var DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/u;
659
+ var IDENTIFIER_PATTERN = /^(?:account|approval|publication|snapshot)_[A-Za-z0-9_-]{20,128}$/u;
660
+ var VERSION_PATTERN = /^[0-9]+\.[0-9]+\.[0-9]+(?:[-+][A-Za-z0-9.-]+)?$/u;
661
+ var RANDOM_SLUG_PATTERN = /^[A-Za-z0-9_-]{40,128}$/u;
662
+ function validDate(value) {
663
+ return typeof value === "string" && Number.isFinite(Date.parse(value));
664
+ }
665
+ function allSafetyChecksPresent(scan) {
666
+ if (!Array.isArray(scan.checks) || scan.checks.length !== STATIC_PROJECT_SAFETY_CHECK_IDS_V1.length) {
667
+ return false;
668
+ }
669
+ const supplied = new Set(scan.checks.map((check) => check.id));
670
+ return STATIC_PROJECT_SAFETY_CHECK_IDS_V1.every((id) => supplied.has(id)) && scan.checks.every((check) => check.passed === true);
671
+ }
672
+ function safeAdventureRender(value) {
673
+ const headings = ["Moonbase Missions", "Forest Rescue Plans", "Ocean Quest Board"];
674
+ const titles = ["Find the moon crystal", "Guide the lost firefly", "Map the coral garden"];
675
+ const days = ["Saturday", "Sunday", "School holiday"];
676
+ const messages = ["Choose a day", "Add a mission title", "Mission ready!"];
677
+ return headings.includes(value.heading) && Array.isArray(value.missions) && value.missions.length >= 1 && value.missions.length <= 3 && value.missions.every((mission2) => titles.includes(mission2.title) && days.includes(mission2.day)) && messages.includes(value.validationMessage) && value.savedAcrossRestart === true && value.accessibleSummaryEnabled === true;
678
+ }
679
+ function safeCreatureRender(value) {
680
+ return ["Moon Moth", "Cloud Cat", "Pebble Dragon"].includes(value.creature) && ["Resting", "Ready to play", "Snack time"].includes(value.status) && [5, 10, 15].includes(value.timerSeconds) && ["single", "cosy-grid", "wide-grid"].includes(value.layout) && value.reducedMotion === true;
681
+ }
682
+ function safeRobotRender(value) {
683
+ return ["scan", "hold-position", "return-to-base"].includes(value.command) && value.safetyConfirmed === true && [1, 2, 4].includes(value.telemetryRate) && ["line", "bars", "text-only"].includes(value.chartMode) && value.serialSimulation === true && ["SCANNING", "HOLDING", "RETURNING"].includes(value.state);
684
+ }
685
+ function validateStaticProjectSnapshot(value) {
686
+ const issues = [];
687
+ if (value.schemaVersion !== "1") issues.push("invalid-schema-version");
688
+ if (value.contractVersion !== STATIC_PROJECT_PUBLISHING_CONTRACT_VERSION_V1) {
689
+ issues.push("invalid-contract-version");
690
+ }
691
+ if (!IDENTIFIER_PATTERN.test(value.snapshotId) || !IDENTIFIER_PATTERN.test(value.subjectAccountId)) {
692
+ issues.push("invalid-identifier");
693
+ }
694
+ if (!value.moduleId.startsWith("junior-coder.") || value.moduleId !== `junior-coder.${value.moduleSlug}`) {
695
+ issues.push("invalid-module");
696
+ }
697
+ if (!VERSION_PATTERN.test(value.moduleVersion)) issues.push("invalid-version");
698
+ if (!DIGEST_PATTERN.test(value.sourceDigest)) issues.push("invalid-source-digest");
699
+ if (!DIGEST_PATTERN.test(value.snapshotDigest)) issues.push("invalid-snapshot-digest");
700
+ if (!Number.isInteger(value.sourceCharacterCount) || value.sourceCharacterCount < 1 || value.sourceCharacterCount > STATIC_PROJECT_MAX_SOURCE_CHARACTERS_V1) {
701
+ issues.push("invalid-source-size");
702
+ }
703
+ if (!Number.isInteger(value.assessmentScore) || value.assessmentScore < 80 || value.assessmentScore > 100) {
704
+ issues.push("assessment-score-incomplete");
705
+ }
706
+ if (value.mandatorySafetyPassed !== true) issues.push("mandatory-safety-missing");
707
+ if (value.scan?.passed !== true || value.scan?.scannerVersion !== STATIC_PROJECT_SCANNER_VERSION_V1) {
708
+ issues.push("scan-not-passed");
709
+ }
710
+ if (!value.scan || !allSafetyChecksPresent(value.scan)) issues.push("scan-checks-incomplete");
711
+ if (value.moduleSlug !== value.renderModel?.kind) issues.push("module-render-model-mismatch");
712
+ const renderSafe = value.renderModel?.kind === "adventure-mission-planner" ? safeAdventureRender(value.renderModel) : value.renderModel?.kind === "creature-care-dashboard" ? safeCreatureRender(value.renderModel) : value.renderModel?.kind === "robot-mission-control" ? safeRobotRender(value.renderModel) : false;
713
+ if (!renderSafe) issues.push("unsafe-render-model");
714
+ if (value.state !== "pending-review" || !validDate(value.createdAt)) issues.push("invalid-created-at");
715
+ return [...new Set(issues)];
716
+ }
717
+ function assertValidStaticProjectSnapshot(value) {
718
+ const issues = validateStaticProjectSnapshot(value);
719
+ if (issues.length > 0) throw new Error(`Invalid static project snapshot: ${issues.join(", ")}`);
720
+ }
721
+ function validateStaticProjectGuardianApproval(value) {
722
+ const issues = [];
723
+ if (value.schemaVersion !== "1") issues.push("invalid-schema-version");
724
+ if (!IDENTIFIER_PATTERN.test(value.approvalId) || !IDENTIFIER_PATTERN.test(value.snapshotId) || !IDENTIFIER_PATTERN.test(value.guardianActorAccountId)) {
725
+ issues.push("invalid-identifier");
726
+ }
727
+ if (!DIGEST_PATTERN.test(value.snapshotDigest)) issues.push("invalid-snapshot-digest");
728
+ if (value.statementVersion !== STATIC_PROJECT_APPROVAL_STATEMENT_VERSION_V1) {
729
+ issues.push("invalid-approval-statement");
730
+ }
731
+ if (!validDate(value.approvedAt)) issues.push("invalid-approved-at");
732
+ return [...new Set(issues)];
733
+ }
734
+ function assertValidStaticProjectGuardianApproval(value) {
735
+ const issues = validateStaticProjectGuardianApproval(value);
736
+ if (issues.length > 0) throw new Error(`Invalid static project approval: ${issues.join(", ")}`);
737
+ }
738
+ function validateStaticProjectPublication(value) {
739
+ const issues = [];
740
+ if (value.schemaVersion !== "1") issues.push("invalid-schema-version");
741
+ if (!IDENTIFIER_PATTERN.test(value.publicationId) || !IDENTIFIER_PATTERN.test(value.snapshotId) || !IDENTIFIER_PATTERN.test(value.approvalId)) {
742
+ issues.push("invalid-identifier");
743
+ }
744
+ if (!DIGEST_PATTERN.test(value.snapshotDigest)) issues.push("invalid-snapshot-digest");
745
+ if (!RANDOM_SLUG_PATTERN.test(value.randomSlug)) issues.push("invalid-random-slug");
746
+ let parsedUrl;
747
+ try {
748
+ parsedUrl = new URL(value.publicUrl);
749
+ } catch {
750
+ parsedUrl = void 0;
751
+ }
752
+ if (!parsedUrl || parsedUrl.protocol !== "https:" || parsedUrl.username || parsedUrl.password) {
753
+ issues.push("invalid-public-url");
754
+ }
755
+ if (!parsedUrl?.pathname.endsWith(`/${value.randomSlug}`)) issues.push("public-url-slug-mismatch");
756
+ if (value.state !== "published") issues.push("published-state-required");
757
+ if (!validDate(value.publishedAt)) issues.push("invalid-published-at");
758
+ if (!validDate(value.expiresAt) || validDate(value.publishedAt) && Date.parse(value.expiresAt) <= Date.parse(value.publishedAt)) {
759
+ issues.push("invalid-publication-expiry");
760
+ }
761
+ if (value.renewedAt !== void 0 && !validDate(value.renewedAt) || value.unpublishedAt !== void 0 && !validDate(value.unpublishedAt)) {
762
+ issues.push("invalid-lifecycle-timestamp");
763
+ }
764
+ return [...new Set(issues)];
765
+ }
766
+ function assertValidStaticProjectPublication(value) {
767
+ const issues = validateStaticProjectPublication(value);
768
+ if (issues.length > 0) throw new Error(`Invalid static project publication: ${issues.join(", ")}`);
769
+ }
770
+
630
771
  // src/rubric-validation.ts
631
772
  var DIMENSION_TOTALS = {
632
773
  structure: 20,
@@ -6707,14 +6848,26 @@ ${summary}`);
6707
6848
  SERVO_CREATURE_MISSION_ONE_AUTHORING_V1,
6708
6849
  SKYWING_SPRINT_MISSION_ONE_AUTHORING_V1,
6709
6850
  STAR_DEFENDER_SQUADRON_MISSION_ONE_AUTHORING_V1,
6851
+ STATIC_PROJECT_APPROVAL_STATEMENT_VERSION_V1,
6852
+ STATIC_PROJECT_DEFAULT_LIFETIME_DAYS_V1,
6853
+ STATIC_PROJECT_MAX_SOURCE_CHARACTERS_V1,
6854
+ STATIC_PROJECT_PUBLISHING_CONTRACT_VERSION_V1,
6855
+ STATIC_PROJECT_SAFETY_CHECK_IDS_V1,
6856
+ STATIC_PROJECT_SCANNER_VERSION_V1,
6710
6857
  VIBE_BUG_DETECTIVE_MISSION_ONE_AUTHORING_V1,
6711
6858
  VIBE_GAME_REMIX_LAB_MISSION_ONE_AUTHORING_V1,
6712
6859
  VIBE_IDEA_STUDIO_MISSION_ONE_AUTHORING_V1,
6713
6860
  assertValidLearningPath,
6714
6861
  assertValidMissionAuthoringBundle,
6862
+ assertValidStaticProjectGuardianApproval,
6863
+ assertValidStaticProjectPublication,
6864
+ assertValidStaticProjectSnapshot,
6715
6865
  calculateAssessment,
6716
6866
  validateAssessmentRubric,
6717
6867
  validateLearningPath,
6718
- validateMissionAuthoringBundle
6868
+ validateMissionAuthoringBundle,
6869
+ validateStaticProjectGuardianApproval,
6870
+ validateStaticProjectPublication,
6871
+ validateStaticProjectSnapshot
6719
6872
  });
6720
6873
  //# sourceMappingURL=index.cjs.map