@thanh01.pmt/curriculum-kit 1.0.0

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 (68) hide show
  1. package/README.md +22 -0
  2. package/dist/ai/index.cjs +4980 -0
  3. package/dist/ai/index.cjs.map +1 -0
  4. package/dist/ai/index.d.cts +1287 -0
  5. package/dist/ai/index.d.ts +1287 -0
  6. package/dist/ai/index.mjs +4924 -0
  7. package/dist/ai/index.mjs.map +1 -0
  8. package/dist/index.cjs +15308 -0
  9. package/dist/index.cjs.map +1 -0
  10. package/dist/index.d.cts +747 -0
  11. package/dist/index.d.ts +747 -0
  12. package/dist/index.mjs +14965 -0
  13. package/dist/index.mjs.map +1 -0
  14. package/dist/media/index.cjs +1238 -0
  15. package/dist/media/index.cjs.map +1 -0
  16. package/dist/media/index.d.cts +254 -0
  17. package/dist/media/index.d.ts +254 -0
  18. package/dist/media/index.mjs +1221 -0
  19. package/dist/media/index.mjs.map +1 -0
  20. package/dist/milestoneBundleGenerator-CbXhs1CP.d.ts +62 -0
  21. package/dist/milestoneBundleGenerator-DrBtHzBO.d.cts +62 -0
  22. package/dist/pipeline/index.cjs +4894 -0
  23. package/dist/pipeline/index.cjs.map +1 -0
  24. package/dist/pipeline/index.d.cts +161 -0
  25. package/dist/pipeline/index.d.ts +161 -0
  26. package/dist/pipeline/index.mjs +4869 -0
  27. package/dist/pipeline/index.mjs.map +1 -0
  28. package/dist/provider-factory-DH3udYlN.d.cts +25 -0
  29. package/dist/provider-factory-DH3udYlN.d.ts +25 -0
  30. package/dist/publishers/index.cjs +575 -0
  31. package/dist/publishers/index.cjs.map +1 -0
  32. package/dist/publishers/index.d.cts +85 -0
  33. package/dist/publishers/index.d.ts +85 -0
  34. package/dist/publishers/index.mjs +561 -0
  35. package/dist/publishers/index.mjs.map +1 -0
  36. package/dist/schemas/index.cjs +1640 -0
  37. package/dist/schemas/index.cjs.map +1 -0
  38. package/dist/schemas/index.d.cts +14194 -0
  39. package/dist/schemas/index.d.ts +14194 -0
  40. package/dist/schemas/index.mjs +1517 -0
  41. package/dist/schemas/index.mjs.map +1 -0
  42. package/dist/standards/index.cjs +910 -0
  43. package/dist/standards/index.cjs.map +1 -0
  44. package/dist/standards/index.d.cts +130 -0
  45. package/dist/standards/index.d.ts +130 -0
  46. package/dist/standards/index.mjs +890 -0
  47. package/dist/standards/index.mjs.map +1 -0
  48. package/dist/standardsCoverageGate-BWAkXY76.d.cts +767 -0
  49. package/dist/standardsCoverageGate-BWAkXY76.d.ts +767 -0
  50. package/dist/storage/index.cjs +595 -0
  51. package/dist/storage/index.cjs.map +1 -0
  52. package/dist/storage/index.d.cts +77 -0
  53. package/dist/storage/index.d.ts +77 -0
  54. package/dist/storage/index.mjs +583 -0
  55. package/dist/storage/index.mjs.map +1 -0
  56. package/dist/streamRunner-C7j5LKon.d.cts +60 -0
  57. package/dist/streamRunner-C7j5LKon.d.ts +60 -0
  58. package/dist/supabasePublisher-C627qXFT.d.cts +63 -0
  59. package/dist/supabasePublisher-C627qXFT.d.ts +63 -0
  60. package/dist/types-BUJGYiep.d.cts +89 -0
  61. package/dist/types-BUJGYiep.d.ts +89 -0
  62. package/dist/workflow/index.cjs +5266 -0
  63. package/dist/workflow/index.cjs.map +1 -0
  64. package/dist/workflow/index.d.cts +295 -0
  65. package/dist/workflow/index.d.ts +295 -0
  66. package/dist/workflow/index.mjs +5216 -0
  67. package/dist/workflow/index.mjs.map +1 -0
  68. package/package.json +105 -0
@@ -0,0 +1,910 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+ var supabaseJs = require('@supabase/supabase-js');
5
+
6
+ // src/standards/frameworkPackSchema.ts
7
+ var BloomHintSchema = zod.z.object({
8
+ process: zod.z.enum(["Remember", "Understand", "Apply", "Analyze", "Evaluate", "Create"]).describe("Cognitive process axis"),
9
+ knowledge: zod.z.enum(["FACTUAL", "CONCEPTUAL", "PROCEDURAL", "METACOGNITIVE"]).optional().describe("Knowledge dimension axis (optional \u2014 most source standards do not tag it)")
10
+ });
11
+ var RecordProvenanceSchema = zod.z.object({
12
+ method: zod.z.enum(["source_official", "ai_draft", "minted_from_content", "human", "crosswalk", "imported"]).describe("source_official = copied from an authoritative machine-readable source; ai_draft = LLM-extracted, REQUIRES human review before activation; minted_from_content = ID synthesized because source has none; crosswalk = derived by the curriculum-crosswalk skill"),
13
+ reviewedBy: zod.z.string().optional().describe("Person/agent that approved this record (required for ai_draft records before activation)"),
14
+ reviewedAt: zod.z.string().optional().describe("ISO-8601 timestamp of review"),
15
+ note: zod.z.string().optional().describe("Free-form provenance note")
16
+ });
17
+ var GradeBandSchema = zod.z.array(zod.z.number().int().min(-1).max(30), { message: "gradeBand must be [minGrade, maxGrade] integers" }).length(2).refine(([min, max]) => min <= max, { message: "gradeBand min must be <= max" }).describe("Canonical numeric grade band. -1 = kindergarten/pre-primary. Framework-specific models (VN grades, US bands, ages) normalize into this scale via manifest.gradeModel");
18
+ var ClassificationSchema = zod.z.object({
19
+ axis: zod.z.string().describe('Axis label, e.g. "strand", "dimension", "Ch\u1EE7 \u0111\u1EC1"'),
20
+ value: zod.z.string().describe('Value on that axis, e.g. "AP", "Disciplinary Core Idea", "Thu\u1EADt to\xE1n"')
21
+ });
22
+ var FrameworkPackManifestSchema = zod.z.object({
23
+ /** Immutable pack identifier. Never reused, never renamed. */
24
+ id: zod.z.string().min(3).regex(/^[a-z0-9][a-z0-9._-]*$/, "pack id must be kebab/lowercase-safe (a-z 0-9 . _ -)").describe('Immutable pack identifier, e.g. "csta-k12-2017", "gdpt-2018-tin-hoc", "abc-tin-hoc"'),
25
+ name: zod.z.string().min(1).describe("Human-readable framework name"),
26
+ specVersion: zod.z.literal("1.0").describe("Version of THIS pack contract (not the content)"),
27
+ contentVersion: zod.z.string().min(1).describe("Content version of the framework inside this pack. Bump on any content change; published versions are immutable"),
28
+ subject: zod.z.string().describe('Primary subject domain, e.g. "computing", "science", "mathematics"'),
29
+ languages: zod.z.array(zod.z.string().min(2)).min(1).describe('BCP-47 language codes present in statement texts, e.g. ["vi"], ["en","vi"]'),
30
+ gradeModel: zod.z.object({
31
+ type: zod.z.enum(["grades", "ages", "bands"]).describe("grades = school grades (canonical numbers); ages = years old; bands = named developmental bands"),
32
+ range: GradeBandSchema.describe("Overall grade range covered by the framework"),
33
+ /** Optional normalization table when type != "grades" (e.g. band "6-8" -> [6,8]). */
34
+ normalization: zod.z.array(
35
+ zod.z.object({
36
+ from: zod.z.string().describe('Source label, e.g. "level 6-8" or "11-13 years"'),
37
+ to: GradeBandSchema
38
+ })
39
+ ).optional().describe('Explicit label -> numeric band conversion table. Required when type != "grades"')
40
+ }),
41
+ provenance: zod.z.object({
42
+ sourceRef: zod.z.string().describe('Citation of the official source, e.g. "Th\xF4ng t\u01B0 32/2018/TT-BGD\u0110T" or a URL'),
43
+ sourceUrl: zod.z.string().optional().describe("Canonical URL of the source document or live registry"),
44
+ license: zod.z.string().describe("License / usage rights of the source text"),
45
+ importedBy: zod.z.string().describe('Who produced this pack, e.g. "platform" or "org:abc"'),
46
+ importedAt: zod.z.string().describe("ISO-8601 timestamp"),
47
+ originalFileHash: zod.z.string().optional().describe("SHA-256 of the original uploaded file, for source provenance")
48
+ }),
49
+ trust: zod.z.enum(["verified", "org"]).default("org").describe("verified = platform-curated; org = self-served by an org. Generation accepts both; compliance reporting counts verified only")
50
+ });
51
+ var FrameworkStatementSchema = zod.z.object({
52
+ /** Canonical, forever-stable statement ID (see invariant I3). */
53
+ id: zod.z.string().min(2).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/, "statement id must not contain whitespace or separators beyond . _ : -").describe('Statement ID. Official source ID when present (e.g. "1B-AP-10"); otherwise minted once from content identity. Namespaced at query time by the pack id, never stored with prefix duplication'),
54
+ /** Optional hierarchical parent within the SAME pack (GDPT competence → requirement, CSTA strand → statement, ...). */
55
+ parentId: zod.z.string().optional().describe("Parent statement id within this pack, when the source framework is hierarchical"),
56
+ gradeBand: GradeBandSchema,
57
+ /** Verbatim canonical text per language. The FIRST / primary language text is judge ground truth (invariant I2). */
58
+ texts: zod.z.record(zod.z.string().min(2), zod.z.string().min(1)).refine((t) => Object.keys(t).length > 0, { message: "texts must contain at least one language entry" }).describe('Verbatim statement text per BCP-47 language key, e.g. {"vi": "Y\xEAu c\u1EA7u c\u1EA7n \u0111\u1EA1t..."}. NEVER paraphrase into these fields'),
59
+ classifications: zod.z.array(ClassificationSchema).optional().default([]).describe("Optional taxonomy axes from the source framework (strands, dimensions, themes...)"),
60
+ bloomHint: BloomHintSchema.optional().describe("Optional Bloom dual-axis hint \u2014 a HINT for generation, never a binding claim about the source"),
61
+ keywords: zod.z.array(zod.z.string()).optional().default([]).describe("Retrieval keywords (topic terms). Used by standards retrieval AND by crosswalk theme matching \u2014 keep them dense"),
62
+ sourceRef: zod.z.string().optional().describe('Precise citation within the source, e.g. "Trang 24, M\xF4n Tin h\u1ECDc l\u1EDBp 6"'),
63
+ provenance: RecordProvenanceSchema
64
+ });
65
+ var MappingKindSchema = zod.z.enum(["covers", "partial", "extends"]).describe('covers = the statement is fully taught when the concept is covered (ONLY "covers" counts toward compliance coverage); partial = overlaps partially; extends = statement goes beyond the concept');
66
+ var FrameworkMappingSchema = zod.z.object({
67
+ statementId: zod.z.string().describe("Must reference a statement id in this pack"),
68
+ /** Namespaced target. Grammar: <namespace>:<ID>. e.g. "ontora:WHILE_LOOP", "ontora:PROBLEM_DECOMPOSITION". Resolved against the target registry at intake — unknown targets FAIL validation. */
69
+ targetRef: zod.z.string().refine((v) => /^[a-z][a-z0-9]*:[A-Za-z0-9][A-Za-z0-9._-]*$/.test(v), 'targetRef must be "<namespace>:<ID>", e.g. "ontora:FOR_LOOP"'),
70
+ kind: MappingKindSchema,
71
+ confidence: zod.z.number().min(0).max(1).describe("0-1 confidence of this mapping"),
72
+ provenance: RecordProvenanceSchema
73
+ });
74
+ var FrameworkPackSchema = zod.z.object({
75
+ manifest: FrameworkPackManifestSchema,
76
+ statements: zod.z.array(FrameworkStatementSchema).min(1).describe("Atomized statements of the framework"),
77
+ mappings: zod.z.array(FrameworkMappingSchema).optional().default([]).describe("Optional bridges to the Ontora knowledge tree (statement-only packs are fully functional)")
78
+ });
79
+ var DUP_OVERLAP_RATIO = 0.8;
80
+ function lintFrameworkPack(pack) {
81
+ const issues = [];
82
+ const statements = pack.statements;
83
+ const ids = /* @__PURE__ */ new Set();
84
+ for (const s of statements) {
85
+ if (ids.has(s.id)) {
86
+ issues.push({ severity: "ERROR", code: "DUP_ID", message: `Duplicate statement id "${s.id}"`, ref: s.id });
87
+ }
88
+ ids.add(s.id);
89
+ }
90
+ for (const s of statements) {
91
+ if (s.parentId && !ids.has(s.parentId)) {
92
+ issues.push({ severity: "ERROR", code: "PARENT_MISSING", message: `parentId "${s.parentId}" does not exist in this pack`, ref: s.id });
93
+ }
94
+ for (const [lang, text] of Object.entries(s.texts)) {
95
+ if (!text || text.trim().length < 8) {
96
+ issues.push({ severity: "ERROR", code: "TEXT_TOO_SHORT", message: `"${lang}" text is empty or shorter than 8 characters`, ref: s.id });
97
+ }
98
+ }
99
+ const [min, max] = s.gradeBand;
100
+ const [gMin, gMax] = pack.manifest.gradeModel.range;
101
+ if (min < gMin || max > gMax) {
102
+ issues.push({ severity: "WARNING", code: "BAND_OUT_OF_RANGE", message: `gradeBand [${min},${max}] falls outside manifest range [${gMin},${gMax}]`, ref: s.id });
103
+ }
104
+ if (s.provenance.method === "ai_draft" && !s.provenance.reviewedBy) {
105
+ issues.push({ severity: "ERROR", code: "AI_DRAFT_UNREVIEWED", message: "ai_draft record has no reviewedBy \u2014 human review required before activation", ref: s.id });
106
+ }
107
+ if (!s.keywords || s.keywords.length === 0) {
108
+ issues.push({ severity: "WARNING", code: "NO_KEYWORDS", message: "No keywords \u2014 statement will be hard to retrieve and to crosswalk", ref: s.id });
109
+ }
110
+ }
111
+ const seen = /* @__PURE__ */ new Map();
112
+ for (const s of statements) {
113
+ const primaryLang = pack.manifest.languages[0];
114
+ const text = s.texts[primaryLang];
115
+ if (!text) continue;
116
+ const norm = text.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/).filter((w) => w.length > 2).sort().join(" ");
117
+ if (!norm) continue;
118
+ const prev = seen.get(norm);
119
+ if (prev !== void 0) {
120
+ issues.push({ severity: "WARNING", code: "DUP_TEXT_EXACT", message: `Text is token-identical to statement "${prev}"`, ref: s.id });
121
+ } else {
122
+ for (const [otherNorm, otherId] of seen.entries()) {
123
+ const a = new Set(norm.split(" "));
124
+ const b = new Set(otherNorm.split(" "));
125
+ let inter = 0;
126
+ for (const w of a) if (b.has(w)) inter++;
127
+ const ratio = inter / Math.max(a.size, b.size);
128
+ if (ratio >= DUP_OVERLAP_RATIO) {
129
+ issues.push({ severity: "WARNING", code: "DUP_TEXT_FUZZY", message: `Text ~${Math.round(ratio * 100)}% overlapping with statement "${otherId}"`, ref: s.id });
130
+ break;
131
+ }
132
+ }
133
+ seen.set(norm, s.id);
134
+ }
135
+ }
136
+ for (const m of pack.mappings ?? []) {
137
+ if (!ids.has(m.statementId)) {
138
+ issues.push({ severity: "ERROR", code: "MAP_STATEMENT_MISSING", message: `mapping references unknown statementId "${m.statementId}"`, ref: m.statementId });
139
+ }
140
+ if (m.provenance.method === "ai_draft" && !m.provenance.reviewedBy) {
141
+ issues.push({ severity: "ERROR", code: "AI_DRAFT_UNREVIEWED", message: `mapping to ${m.targetRef} is ai_draft without reviewedBy`, ref: m.statementId });
142
+ }
143
+ if (m.kind === "covers" && m.confidence < 0.5) {
144
+ issues.push({ severity: "WARNING", code: "COVERS_LOW_CONFIDENCE", message: `kind="covers" with confidence ${m.confidence} < 0.5 \u2014 consider "partial"`, ref: m.statementId });
145
+ }
146
+ }
147
+ return issues;
148
+ }
149
+ function validateFrameworkPack(input) {
150
+ const parsed = FrameworkPackSchema.safeParse(input);
151
+ if (!parsed.success) {
152
+ return {
153
+ valid: false,
154
+ schemaErrors: parsed.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`),
155
+ lint: [],
156
+ stats: { statements: 0, mappings: 0, coveredStatements: 0, statementOnlyStatements: 0, gradeRange: [0, 0] }
157
+ };
158
+ }
159
+ const pack = parsed.data;
160
+ const lint = lintFrameworkPack(pack);
161
+ const covered = new Set((pack.mappings ?? []).map((m) => m.statementId));
162
+ const bands = pack.statements.map((s) => s.gradeBand);
163
+ return {
164
+ valid: lint.every((i) => i.severity !== "ERROR"),
165
+ schemaErrors: [],
166
+ lint,
167
+ stats: {
168
+ statements: pack.statements.length,
169
+ mappings: pack.mappings?.length ?? 0,
170
+ coveredStatements: covered.size,
171
+ statementOnlyStatements: pack.statements.length - covered.size,
172
+ gradeRange: [Math.min(...bands.flat()), Math.max(...bands.flat())]
173
+ }
174
+ };
175
+ }
176
+
177
+ // src/standards/standardsContextInjector.ts
178
+ var DEFAULT_MAX_STATEMENTS = 20;
179
+ function normalizeBand(band) {
180
+ return Array.isArray(band) ? band : [band, band];
181
+ }
182
+ function tokenize(text) {
183
+ return new Set(
184
+ text.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/).filter((w) => w.length > 2)
185
+ );
186
+ }
187
+ function selectStatementsForLesson(input) {
188
+ const max = input.maxStatements ?? DEFAULT_MAX_STATEMENTS;
189
+ const lessonBand = normalizeBand(input.gradeBand);
190
+ const lessonTokens = tokenize(input.topicText);
191
+ const conceptSet = new Set((input.conceptCodes ?? []).map((c) => c.toUpperCase().trim()).filter(Boolean));
192
+ const candidates = [];
193
+ for (const pack of input.packs) {
194
+ for (const statement of pack.statements) {
195
+ const [sMin, sMax] = statement.gradeBand;
196
+ const overlaps = Math.max(sMin, lessonBand[0]) <= Math.min(sMax, lessonBand[1]);
197
+ if (!overlaps) continue;
198
+ const matchedBy = ["band"];
199
+ let score = 1;
200
+ const kwTokens = new Set(statement.keywords.map((k) => k.toLowerCase()));
201
+ let kwHits = 0;
202
+ for (const t of lessonTokens) if (kwTokens.has(t)) kwHits++;
203
+ if (kwHits > 0) {
204
+ matchedBy.push("keyword");
205
+ score += 2 + kwHits * 0.5;
206
+ }
207
+ if (conceptSet.size > 0) {
208
+ for (const k of statement.keywords) {
209
+ if (conceptSet.has(k.toUpperCase().replace(/\s+/g, "_"))) {
210
+ matchedBy.push("concept");
211
+ score += 3;
212
+ break;
213
+ }
214
+ }
215
+ }
216
+ let textHits = 0;
217
+ for (const lang of Object.keys(statement.texts)) {
218
+ const st = tokenize(statement.texts[lang]);
219
+ for (const t of lessonTokens) if (st.has(t)) textHits++;
220
+ }
221
+ if (textHits >= 2) {
222
+ matchedBy.push("text");
223
+ score += Math.min(2, textHits * 0.2);
224
+ }
225
+ if (score > 1) candidates.push({ packId: pack.manifest.id, statement, score, matchedBy });
226
+ }
227
+ }
228
+ return candidates.sort((a, b) => b.score - a.score).slice(0, max);
229
+ }
230
+ function buildStandardsContextBlock(selected, opts) {
231
+ if (selected.length === 0) return "";
232
+ const lang = opts?.language;
233
+ const rows = selected.map(({ packId, statement }) => {
234
+ const text = lang && statement.texts[lang] || Object.values(statement.texts)[0];
235
+ const band = statement.gradeBand[0] === statement.gradeBand[1] ? `L${statement.gradeBand[0]}` : `L${statement.gradeBand[0]}-L${statement.gradeBand[1]}`;
236
+ return `- [${packId}:${statement.id} | ${band}] "${text}"`;
237
+ }).join("\n");
238
+ return [
239
+ "# STANDARDS CONTEXT (SOURCE OF TRUTH \u2014 VERBATIM STATEMENTS)",
240
+ "",
241
+ "The statements below are COPIED VERBATIM from official/framework standard documents adopted for this course.",
242
+ "They are BINDING targets, not suggestions:",
243
+ "- Every Learning Objective you write MUST be anchored to one or more statement IDs above via standardRefs.",
244
+ "- You MUST use ONLY statement IDs from this list. NEVER invent, paraphrase IDs, or cite standards not present here.",
245
+ "- The statement TEXT is the required destination: activities and assessments must demonstrably lead a learner to it.",
246
+ "- The statement is the DESTINATION, not a script: you may extend beyond it, but you may not skip or contradict it.",
247
+ "",
248
+ "## Statement list",
249
+ rows
250
+ ].join("\n");
251
+ }
252
+ function buildStandardsContext(input) {
253
+ const selected = selectStatementsForLesson(input);
254
+ return { block: buildStandardsContextBlock(selected, { language: input.language }), selected };
255
+ }
256
+
257
+ // src/standards/standardsCoverageGate.ts
258
+ function resolveStatementRef(ref, packs) {
259
+ const [head, ...rest] = ref.split(":");
260
+ const candidatePacks = rest.length > 0 ? packs.filter((p) => p.manifest.id === head) : packs;
261
+ const statementId = rest.length > 0 ? rest.join(":") : ref;
262
+ for (const p of candidatePacks) {
263
+ if (p.statements.some((s) => s.id === statementId)) return { pack: p, statementId };
264
+ }
265
+ return null;
266
+ }
267
+ function evaluateStandardsCoverage(input) {
268
+ const rows = [];
269
+ const aoToLo = /* @__PURE__ */ new Map();
270
+ for (const ao of input.assessmentObjectives ?? []) aoToLo.set(ao.aoCode, ao.alignedLO);
271
+ const loToRefs = /* @__PURE__ */ new Map();
272
+ for (const lo of input.objectives) {
273
+ loToRefs.set(lo.code, new Set(lo.standardRefs ?? []));
274
+ }
275
+ const taughtLOs = /* @__PURE__ */ new Set();
276
+ for (const act of input.activities ?? []) for (const lo of act.lo) taughtLOs.add(lo);
277
+ const assessedLOs = /* @__PURE__ */ new Set();
278
+ for (const q of input.quizQuestions) {
279
+ if (q.alignedLO) assessedLOs.add(q.alignedLO);
280
+ if (q.alignedAO) {
281
+ const lo = aoToLo.get(q.alignedAO);
282
+ if (lo) assessedLOs.add(lo);
283
+ }
284
+ }
285
+ for (const pack of input.packs) {
286
+ const mappingByStatement = /* @__PURE__ */ new Map();
287
+ for (const m of pack.mappings ?? []) {
288
+ const prev = mappingByStatement.get(m.statementId);
289
+ if (!prev || prev !== "covers" && m.kind === "covers" || prev === "extends" && m.kind !== "extends") {
290
+ mappingByStatement.set(m.statementId, m.kind);
291
+ }
292
+ }
293
+ for (const statement of pack.statements) {
294
+ const refFull = `${pack.manifest.id}:${statement.id}`;
295
+ const issues = [];
296
+ const los = input.objectives.filter((lo) => (lo.standardRefs ?? []).some((r) => r === statement.id || r === refFull)).map((lo) => lo.code);
297
+ const kind = mappingByStatement.get(statement.id);
298
+ const hasMapping = kind !== void 0;
299
+ const isComplianceRelevant = kind === "covers";
300
+ const hasActivity = los.some((lo) => taughtLOs.has(lo));
301
+ const hasAssessment = los.some((lo) => assessedLOs.has(lo));
302
+ let status;
303
+ if (!hasMapping) status = "UNMAPPED";
304
+ else if (!isComplianceRelevant) status = "PARTIAL";
305
+ else if (los.length > 0 && hasActivity && hasAssessment) status = "COVERED";
306
+ else status = "UNCOVERED";
307
+ if (status === "UNCOVERED") {
308
+ if (los.length === 0) issues.push("No learning objective anchors this statement (missing standardRefs).");
309
+ else {
310
+ if (!hasActivity) issues.push(`Anchored LO(s) ${los.join(", ")} have no activity in lesson flow.`);
311
+ if (!hasAssessment) issues.push(`Anchored LO(s) ${los.join(", ")} are not measured by any quiz question.`);
312
+ }
313
+ }
314
+ rows.push({
315
+ packId: pack.manifest.id,
316
+ statementId: statement.id,
317
+ statementText: Object.values(statement.texts)[0] ?? "",
318
+ status,
319
+ objectives: los,
320
+ hasActivity,
321
+ hasAssessment,
322
+ issues
323
+ });
324
+ }
325
+ }
326
+ for (const lo of input.objectives) {
327
+ for (const r of lo.standardRefs ?? []) {
328
+ if (!resolveStatementRef(r, input.packs)) {
329
+ rows.push({
330
+ packId: "(unresolved)",
331
+ statementId: r,
332
+ statementText: "",
333
+ status: "UNCOVERED",
334
+ objectives: [lo.code],
335
+ hasActivity: false,
336
+ hasAssessment: false,
337
+ issues: [`standardRef "${r}" does not exist in any adopted pack \u2014 likely a hallucinated or stale ID`]
338
+ });
339
+ }
340
+ }
341
+ }
342
+ const complianceRows = rows.filter((r) => r.status !== "UNMAPPED" && r.status !== "PARTIAL");
343
+ const covered = complianceRows.filter((r) => r.status === "COVERED").length;
344
+ const coveragePct = complianceRows.length === 0 ? 100 : Math.round(covered / complianceRows.length * 100);
345
+ const unresolvedCount = rows.filter((r) => r.packId === "(unresolved)").length;
346
+ const verdict = unresolvedCount > 0 || coveragePct < 60 ? "FAIL" : coveragePct < 100 ? "WARN" : "PASS";
347
+ const lines = [
348
+ "# Standards Coverage Report",
349
+ "",
350
+ `- Verdict: **${verdict}**`,
351
+ `- Compliance coverage: **${coveragePct}%** (${covered}/${complianceRows.length} "covers" statements fully taught + assessed)`,
352
+ `- Unresolved standardRefs: ${unresolvedCount}`,
353
+ "",
354
+ "| Pack | Statement | Status | LOs | Activity | Assessment |",
355
+ "|---|---|---|---|---|---|",
356
+ ...rows.map(
357
+ (r) => `|${r.packId}|${r.statementId}|${r.status}|${r.objectives.join(", ") || "\u2014"}|${r.hasActivity ? "\u2713" : "\u2717"}|${r.hasAssessment ? "\u2713" : "\u2717"}|`
358
+ )
359
+ ];
360
+ const issueLines = rows.filter((r) => r.issues.length > 0).flatMap((r) => r.issues.map((i) => `- [${r.packId}:${r.statementId}] ${i}`));
361
+ if (issueLines.length > 0) lines.push("", "## Issues", ...issueLines);
362
+ return {
363
+ verdict,
364
+ coveragePct,
365
+ rows,
366
+ summary: `Standards coverage ${coveragePct}% \u2014 verdict ${verdict}`,
367
+ rawMarkdownReport: lines.join("\n")
368
+ };
369
+ }
370
+ var StandardsRegistryAdapter = class {
371
+ client;
372
+ constructor(config = {}) {
373
+ if (config.client) {
374
+ this.client = config.client;
375
+ return;
376
+ }
377
+ const url = config.supabaseUrl || process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL || "";
378
+ const key = config.supabaseServiceRoleKey || process.env.SUPABASE_SERVICE_ROLE_KEY || config.supabaseAnonKey || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "";
379
+ if (!url || !key) {
380
+ throw new Error("StandardsRegistryAdapter: Supabase URL/key missing (pass client or env).");
381
+ }
382
+ this.client = supabaseJs.createClient(url, key);
383
+ }
384
+ // ─── Intake (write path — service role) ───────────────────────────────────
385
+ /** Persist a schema-validated pack as a new framework (status=draft). */
386
+ async importPack(pack, opts) {
387
+ const parsed = FrameworkPackSchema.safeParse(pack);
388
+ if (!parsed.success) {
389
+ throw new Error("importPack: pack failed schema validation: " + parsed.error.issues.map((i) => i.path.join(".") + ": " + i.message).join("; "));
390
+ }
391
+ const p = parsed.data;
392
+ const { data: fw, error: fwErr } = await this.client.from("standards_frameworks").insert({
393
+ pack_id: p.manifest.id,
394
+ content_version: p.manifest.contentVersion,
395
+ name: p.manifest.name,
396
+ spec_version: p.manifest.specVersion,
397
+ subject: p.manifest.subject,
398
+ languages: p.manifest.languages,
399
+ grade_model: p.manifest.gradeModel,
400
+ provenance: p.manifest.provenance,
401
+ trust: p.manifest.trust,
402
+ status: "draft",
403
+ organization_code: opts?.organizationCode ?? (p.manifest.trust === "verified" ? null : p.manifest.provenance.importedBy?.replace(/^org:/, "") || null),
404
+ original_file_path: opts?.originalFilePath ?? null,
405
+ created_by: opts?.createdBy ?? null
406
+ }).select("id").single();
407
+ if (fwErr) throw new Error("importPack: framework insert failed: " + fwErr.message);
408
+ const frameworkId = fw.id;
409
+ const statementRows = p.statements.map((s) => ({
410
+ framework_id: frameworkId,
411
+ statement_id: s.id,
412
+ parent_statement_id: s.parentId ?? null,
413
+ grade_min: s.gradeBand[0],
414
+ grade_max: s.gradeBand[1],
415
+ texts: s.texts,
416
+ classifications: s.classifications ?? [],
417
+ bloom_hint: s.bloomHint ?? null,
418
+ keywords: s.keywords ?? [],
419
+ source_ref: s.sourceRef ?? null,
420
+ provenance: s.provenance
421
+ }));
422
+ const { error: stErr, count: stCount } = await this.client.from("standards_statements").insert(statementRows, { count: "exact" });
423
+ if (stErr) throw new Error("importPack: statement insert failed: " + stErr.message);
424
+ let mappingCount = 0;
425
+ if (p.mappings && p.mappings.length > 0) {
426
+ const mappingRows = p.mappings.map((m) => ({
427
+ framework_id: frameworkId,
428
+ statement_id: m.statementId,
429
+ target_ref: m.targetRef,
430
+ kind: m.kind,
431
+ confidence: m.confidence,
432
+ provenance: m.provenance
433
+ }));
434
+ const { error: mpErr, count: mpCount } = await this.client.from("standards_mappings").insert(mappingRows, { count: "exact" });
435
+ if (mpErr) throw new Error("importPack: mapping insert failed: " + mpErr.message);
436
+ mappingCount = mpCount ?? mappingRows.length;
437
+ }
438
+ return { frameworkDbId: frameworkId, statementsInserted: stCount ?? statementRows.length, mappingsInserted: mappingCount };
439
+ }
440
+ /** Activate a draft framework (immutable version is now live). */
441
+ async activatePack(frameworkDbId) {
442
+ const { error } = await this.client.from("standards_frameworks").update({ status: "active" }).eq("id", frameworkDbId).eq("status", "draft");
443
+ if (error) throw new Error("activatePack failed: " + error.message);
444
+ }
445
+ async deprecatePack(frameworkDbId) {
446
+ const { error } = await this.client.from("standards_frameworks").update({ status: "deprecated" }).eq("id", frameworkDbId);
447
+ if (error) throw new Error("deprecatePack failed: " + error.message);
448
+ }
449
+ async adoptPack(frameworkDbId, organizationCode, adoptedBy) {
450
+ const { error } = await this.client.from("standards_org_adoptions").upsert(
451
+ { framework_id: frameworkDbId, organization_code: organizationCode, adopted_by: adoptedBy ?? null },
452
+ { onConflict: "organization_code,framework_id" }
453
+ );
454
+ if (error) throw new Error("adoptPack failed: " + error.message);
455
+ }
456
+ // ─── Runtime (read path — generation) ─────────────────────────────────────
457
+ /**
458
+ * Load all packs usable by an org: platform packs (verified, org IS NULL) +
459
+ * org packs + org adoptions. Hydrated into the same FrameworkPack shape the
460
+ * in-memory Phase-1 components consume (injector / coverage gate / judge).
461
+ */
462
+ async loadPacksForOrg(organizationCode) {
463
+ let query = this.client.from("standards_frameworks").select("*, standards_org_adoptions(organization_code)").eq("status", "active");
464
+ const { data: frameworks, error } = await query;
465
+ if (error) throw new Error("loadPacksForOrg: " + error.message);
466
+ const visible = (frameworks ?? []).filter((f) => {
467
+ const own = f.organization_code && organizationCode && f.organization_code === organizationCode;
468
+ const global = !f.organization_code;
469
+ const adopted = organizationCode && (f.standards_org_adoptions ?? []).some((a) => a.organization_code === organizationCode);
470
+ return global || own || adopted;
471
+ });
472
+ if (visible.length === 0) return [];
473
+ const ids = visible.map((f) => f.id);
474
+ const [{ data: statements, error: stErr }, { data: mappings, error: mpErr }, { data: overrides, error: ovErr }] = await Promise.all([
475
+ this.client.from("standards_statements").select("*").in("framework_id", ids),
476
+ this.client.from("standards_mappings").select("*").in("framework_id", ids),
477
+ this.client.from("standards_mapping_overrides").select("*").in("framework_id", ids)
478
+ ]);
479
+ if (stErr) throw new Error("loadPacksForOrg statements: " + stErr.message);
480
+ const overridesByFw = /* @__PURE__ */ new Map();
481
+ for (const o of overrides ?? []) {
482
+ const list = overridesByFw.get(o.framework_id) ?? [];
483
+ list.push(o);
484
+ overridesByFw.set(o.framework_id, list);
485
+ }
486
+ return visible.map((f) => {
487
+ const stmts = (statements ?? []).filter((s) => s.framework_id === f.id).map((s) => ({
488
+ id: s.statement_id,
489
+ parentId: s.parent_statement_id ?? void 0,
490
+ gradeBand: [s.grade_min, s.grade_max],
491
+ texts: s.texts,
492
+ classifications: s.classifications ?? [],
493
+ bloomHint: s.bloom_hint ?? void 0,
494
+ keywords: s.keywords ?? [],
495
+ sourceRef: s.source_ref ?? void 0,
496
+ provenance: s.provenance ?? { method: "imported" }
497
+ }));
498
+ const ov = overridesByFw.get(f.id) ?? [];
499
+ const retired = new Set(ov.filter((o) => o.kind === "retired").map((o) => o.statement_id + "|" + o.target_ref));
500
+ const embedded = (mappings ?? []).filter((m) => m.framework_id === f.id).map((m) => ({
501
+ statementId: m.statement_id,
502
+ targetRef: m.target_ref,
503
+ kind: m.kind,
504
+ confidence: Number(m.confidence),
505
+ provenance: m.provenance ?? { method: "imported" }
506
+ }));
507
+ const overlay = ov.filter((o) => o.kind !== "retired").map((o) => ({
508
+ statementId: o.statement_id,
509
+ targetRef: o.target_ref,
510
+ kind: o.kind,
511
+ confidence: Number(o.confidence),
512
+ provenance: { method: "human", reviewedBy: o.created_by ?? "overlay", note: o.note ?? void 0 }
513
+ }));
514
+ const bridge = /* @__PURE__ */ new Map();
515
+ for (const m of embedded) bridge.set(m.statementId + "|" + m.targetRef, m);
516
+ for (const m of overlay) bridge.set(m.statementId + "|" + m.targetRef, m);
517
+ const finalMappings = [...bridge.values()].filter((m) => !retired.has(m.statementId + "|" + m.targetRef));
518
+ const hydrated = FrameworkPackSchema.safeParse({
519
+ manifest: {
520
+ id: f.pack_id,
521
+ name: f.name,
522
+ specVersion: f.spec_version || "1.0",
523
+ contentVersion: f.content_version,
524
+ subject: f.subject,
525
+ languages: f.languages,
526
+ gradeModel: f.grade_model,
527
+ provenance: f.provenance,
528
+ trust: f.trust
529
+ },
530
+ statements: stmts,
531
+ mappings: finalMappings
532
+ });
533
+ if (!hydrated.success) {
534
+ throw new Error('loadPacksForOrg: hydrated pack "' + f.pack_id + '" failed schema: ' + hydrated.error.issues[0]?.message);
535
+ }
536
+ return { ...hydrated.data, dbId: f.id, status: f.status, organizationCode: f.organization_code ?? null };
537
+ });
538
+ }
539
+ };
540
+
541
+ // src/standards/standardStackGenerator.ts
542
+ function bandLabel(band) {
543
+ return band[0] === band[1] ? `L${band[0]}` : `L${band[0]}\u2013L${band[1]}`;
544
+ }
545
+ function buildStandardStackMarkdown(opts) {
546
+ const { projectId, packs, courseGradeBand, language } = opts;
547
+ const lines = [];
548
+ lines.push("---");
549
+ lines.push(`id: "${projectId.toUpperCase()}-STANDARD-STACK"`);
550
+ lines.push(`title: "Standard Stack: ${projectId}"`);
551
+ lines.push('type: "STANDARD_STACK"');
552
+ lines.push('phase: "P1"');
553
+ lines.push('version: "v2.0-deterministic"');
554
+ lines.push(`generatedAt: "${(/* @__PURE__ */ new Date()).toISOString()}"`);
555
+ lines.push('source: "Standards Registry (deterministic render \u2014 not LLM-generated)"');
556
+ lines.push("---");
557
+ lines.push("");
558
+ lines.push("# STANDARD STACK \u2014 H\u1EC7 ti\xEAu chu\u1EA9n tham chi\u1EBFu");
559
+ lines.push("");
560
+ if (packs.length === 0) {
561
+ lines.push("> \u26A0\uFE0F No Framework Pack adopted for this course. Generation will run WITHOUT standards grounding");
562
+ lines.push("> (no statement anchoring, no Standards Alignment judge dimension, no coverage gate).");
563
+ lines.push("> Import a pack via the Standards Registry before generating for standards-bound courses.");
564
+ return lines.join("\n");
565
+ }
566
+ for (const pack of packs) {
567
+ const m = pack.manifest;
568
+ lines.push(`## ${m.name}`);
569
+ lines.push("");
570
+ lines.push(`- **Pack ID:** \`${m.id}\` (content ${m.contentVersion}, contract v${m.specVersion})`);
571
+ lines.push(`- **Trust:** ${m.trust} \u2014 ${m.trust === "verified" ? "platform-curated" : "org-imported"}`);
572
+ lines.push(`- **Ngu\u1ED3n:** ${m.provenance.sourceRef}${m.provenance.sourceUrl ? " (" + m.provenance.sourceUrl + ")" : ""}`);
573
+ lines.push(`- **License:** ${m.provenance.license}`);
574
+ lines.push(`- **Grade range (manifest):** ${bandLabel(m.gradeModel.range)}`);
575
+ lines.push("");
576
+ lines.push("### Statements (verbatim \u2014 binding targets)");
577
+ lines.push("");
578
+ lines.push("| ID | Band | Text (verbatim) | Keywords |");
579
+ lines.push("|---|---|---|---|");
580
+ const statements = courseGradeBand ? pack.statements.filter((s) => Math.max(s.gradeBand[0], courseGradeBand[0]) <= Math.min(s.gradeBand[1], courseGradeBand[1])) : pack.statements;
581
+ for (const s of statements) {
582
+ const text = language && s.texts[language] || Object.values(s.texts)[0];
583
+ const mapped = (pack.mappings ?? []).filter((mp) => mp.statementId === s.id);
584
+ const mapNote = mapped.length > 0 ? ` \u2192 ${mapped.map((mp) => mp.targetRef + " (" + mp.kind + ")").join(", ")}` : "";
585
+ lines.push(`| \`${s.id}\` | ${bandLabel(s.gradeBand)} | ${text.replace(/\|/g, "\\|")} | ${(s.keywords || []).join(", ")}${mapNote ? " |" + mapNote : " |"}`);
586
+ }
587
+ lines.push("");
588
+ }
589
+ lines.push("---");
590
+ lines.push("");
591
+ lines.push("*Document rendered deterministically from the Standards Registry. Statement texts are COPIED VERBATIM from the cited sources and are binding for generation, judging, and coverage reporting.*");
592
+ return lines.join("\n");
593
+ }
594
+
595
+ // src/standards/data/csta-k12-2017.json
596
+ var csta_k12_2017_default = {
597
+ manifest: {
598
+ id: "csta-k12-2017",
599
+ name: "CSTA K-12 Computer Science Standards (2017)",
600
+ specVersion: "1.0",
601
+ contentVersion: "2017.1",
602
+ subject: "computing",
603
+ languages: [
604
+ "en"
605
+ ],
606
+ gradeModel: {
607
+ type: "grades",
608
+ range: [
609
+ 3,
610
+ 12
611
+ ]
612
+ },
613
+ provenance: {
614
+ sourceRef: "CSTA K-12 Computer Science Standards, Revised 2017 - csteachers.org",
615
+ sourceUrl: "https://www.csteachers.org/page/standards",
616
+ license: "CC BY-NC-SA 4.0 (cited verbatim for alignment use)",
617
+ importedBy: "platform",
618
+ importedAt: "2026-08-29T00:00:00.000Z"
619
+ },
620
+ trust: "verified"
621
+ },
622
+ statements: [
623
+ {
624
+ id: "1B-AP-08",
625
+ gradeBand: [
626
+ 6,
627
+ 8
628
+ ],
629
+ texts: {
630
+ en: "Demonstrate that programs can be combined with other programs to create new solutions."
631
+ },
632
+ classifications: [
633
+ {
634
+ axis: "strand",
635
+ value: "Algorithms & Programming"
636
+ }
637
+ ],
638
+ bloomHint: {
639
+ process: "Apply"
640
+ },
641
+ keywords: [
642
+ "combine",
643
+ "programs",
644
+ "composition",
645
+ "reuse",
646
+ "modules"
647
+ ],
648
+ sourceRef: "CSTA K-12 2017, Level 1B (Grades 6-8), Algorithms & Programming",
649
+ provenance: {
650
+ method: "source_official"
651
+ }
652
+ },
653
+ {
654
+ id: "1B-AP-10",
655
+ gradeBand: [
656
+ 6,
657
+ 8
658
+ ],
659
+ texts: {
660
+ en: "Create programs that include sequences, events, loops, and conditionals."
661
+ },
662
+ classifications: [
663
+ {
664
+ axis: "strand",
665
+ value: "Algorithms & Programming"
666
+ }
667
+ ],
668
+ bloomHint: {
669
+ process: "Create",
670
+ knowledge: "PROCEDURAL"
671
+ },
672
+ keywords: [
673
+ "sequence",
674
+ "events",
675
+ "loops",
676
+ "conditionals",
677
+ "control structures",
678
+ "for",
679
+ "while"
680
+ ],
681
+ sourceRef: "CSTA K-12 2017, Level 1B (Grades 6-8), Algorithms & Programming",
682
+ provenance: {
683
+ method: "source_official"
684
+ }
685
+ },
686
+ {
687
+ id: "1B-AP-11",
688
+ gradeBand: [
689
+ 6,
690
+ 8
691
+ ],
692
+ texts: {
693
+ en: "Decompose (break down) problems into smaller, manageable subproblems to facilitate the program development process."
694
+ },
695
+ classifications: [
696
+ {
697
+ axis: "strand",
698
+ value: "Algorithms & Programming"
699
+ }
700
+ ],
701
+ bloomHint: {
702
+ process: "Analyze"
703
+ },
704
+ keywords: [
705
+ "decomposition",
706
+ "subproblems",
707
+ "problem breakdown",
708
+ "algorithm",
709
+ "divide"
710
+ ],
711
+ sourceRef: "CSTA K-12 2017, Level 1B (Grades 6-8), Algorithms & Programming",
712
+ provenance: {
713
+ method: "source_official"
714
+ }
715
+ },
716
+ {
717
+ id: "1B-AP-12",
718
+ gradeBand: [
719
+ 6,
720
+ 8
721
+ ],
722
+ texts: {
723
+ en: "Modify, remix, or include portions of other programs in one's own work to develop something new or add more advanced features."
724
+ },
725
+ classifications: [
726
+ {
727
+ axis: "strand",
728
+ value: "Algorithms & Programming"
729
+ }
730
+ ],
731
+ bloomHint: {
732
+ process: "Create"
733
+ },
734
+ keywords: [
735
+ "remix",
736
+ "modify",
737
+ "iterate",
738
+ "extend programs"
739
+ ],
740
+ sourceRef: "CSTA K-12 2017, Level 1B (Grades 6-8), Algorithms & Programming",
741
+ provenance: {
742
+ method: "source_official"
743
+ }
744
+ },
745
+ {
746
+ id: "1B-AP-15",
747
+ gradeBand: [
748
+ 6,
749
+ 8
750
+ ],
751
+ texts: {
752
+ en: "Test and debug (identify and fix errors) a program or algorithm to ensure it runs as intended."
753
+ },
754
+ classifications: [
755
+ {
756
+ axis: "strand",
757
+ value: "Algorithms & Programming"
758
+ }
759
+ ],
760
+ bloomHint: {
761
+ process: "Evaluate"
762
+ },
763
+ keywords: [
764
+ "test",
765
+ "debug",
766
+ "errors",
767
+ "troubleshooting",
768
+ "fix"
769
+ ],
770
+ sourceRef: "CSTA K-12 2017, Level 1B (Grades 6-8), Algorithms & Programming",
771
+ provenance: {
772
+ method: "source_official"
773
+ }
774
+ },
775
+ {
776
+ id: "1B-IC-21",
777
+ gradeBand: [
778
+ 6,
779
+ 8
780
+ ],
781
+ texts: {
782
+ en: "Use public domain or creative commons media, and refrain from copying or using material created by others without permission."
783
+ },
784
+ classifications: [
785
+ {
786
+ axis: "strand",
787
+ value: "Impacts of Computing"
788
+ }
789
+ ],
790
+ bloomHint: {
791
+ process: "Apply"
792
+ },
793
+ keywords: [
794
+ "copyright",
795
+ "creative commons",
796
+ "public domain",
797
+ "digital citizenship",
798
+ "license"
799
+ ],
800
+ sourceRef: "CSTA K-12 2017, Level 1B (Grades 6-8), Impacts of Computing",
801
+ provenance: {
802
+ method: "source_official"
803
+ }
804
+ }
805
+ ],
806
+ mappings: [
807
+ {
808
+ statementId: "1B-AP-10",
809
+ targetRef: "ontora:FOR_LOOP",
810
+ kind: "covers",
811
+ confidence: 0.85,
812
+ provenance: {
813
+ method: "human",
814
+ reviewedBy: "thanh01pmt"
815
+ }
816
+ },
817
+ {
818
+ statementId: "1B-AP-10",
819
+ targetRef: "ontora:WHILE_LOOP",
820
+ kind: "covers",
821
+ confidence: 0.85,
822
+ provenance: {
823
+ method: "human",
824
+ reviewedBy: "thanh01pmt"
825
+ }
826
+ },
827
+ {
828
+ statementId: "1B-AP-11",
829
+ targetRef: "ontora:PROBLEM_DECOMPOSITION",
830
+ kind: "covers",
831
+ confidence: 0.95,
832
+ provenance: {
833
+ method: "human",
834
+ reviewedBy: "thanh01pmt"
835
+ }
836
+ },
837
+ {
838
+ statementId: "1B-AP-08",
839
+ targetRef: "ontora:EVENT_BASED_PROGRAMMING",
840
+ kind: "partial",
841
+ confidence: 0.7,
842
+ provenance: {
843
+ method: "ai_draft",
844
+ reviewedBy: "thanh01pmt"
845
+ }
846
+ },
847
+ {
848
+ statementId: "1B-AP-15",
849
+ targetRef: "ontora:TROUBLESHOOTING_STEPS",
850
+ kind: "covers",
851
+ confidence: 0.9,
852
+ provenance: {
853
+ method: "ai_draft",
854
+ reviewedBy: "thanh01pmt"
855
+ }
856
+ }
857
+ ]
858
+ };
859
+
860
+ // src/standards/bundledPacks.ts
861
+ var BUNDLED_PACK_RECORDS = {
862
+ "csta-k12-2017": csta_k12_2017_default
863
+ };
864
+ var BUNDLED_STANDARDS_PACK_IDS = Object.keys(BUNDLED_PACK_RECORDS).sort();
865
+ var parsedPackCache = /* @__PURE__ */ new Map();
866
+ function resolveStandardsPacks(packIds) {
867
+ if (!packIds || packIds.length === 0) return [];
868
+ const packs = [];
869
+ for (const id of packIds) {
870
+ if (parsedPackCache.has(id)) {
871
+ packs.push(parsedPackCache.get(id));
872
+ continue;
873
+ }
874
+ const raw = BUNDLED_PACK_RECORDS[id];
875
+ if (!raw) {
876
+ console.warn(`[standards] pack '${id}' kh\xF4ng t\u1ED3n t\u1EA1i trong kit \u2014 b\u1ECF qua.`);
877
+ continue;
878
+ }
879
+ const parsed = FrameworkPackSchema.safeParse(raw);
880
+ if (!parsed.success) {
881
+ console.warn(`[standards] pack '${id}' fail schema \u2014 b\u1ECF qua:`, parsed.error.issues[0]?.message);
882
+ continue;
883
+ }
884
+ parsedPackCache.set(id, parsed.data);
885
+ packs.push(parsed.data);
886
+ }
887
+ return packs;
888
+ }
889
+
890
+ exports.BUNDLED_STANDARDS_PACK_IDS = BUNDLED_STANDARDS_PACK_IDS;
891
+ exports.BloomHintSchema = BloomHintSchema;
892
+ exports.ClassificationSchema = ClassificationSchema;
893
+ exports.FrameworkMappingSchema = FrameworkMappingSchema;
894
+ exports.FrameworkPackManifestSchema = FrameworkPackManifestSchema;
895
+ exports.FrameworkPackSchema = FrameworkPackSchema;
896
+ exports.FrameworkStatementSchema = FrameworkStatementSchema;
897
+ exports.GradeBandSchema = GradeBandSchema;
898
+ exports.MappingKindSchema = MappingKindSchema;
899
+ exports.RecordProvenanceSchema = RecordProvenanceSchema;
900
+ exports.StandardsRegistryAdapter = StandardsRegistryAdapter;
901
+ exports.buildStandardStackMarkdown = buildStandardStackMarkdown;
902
+ exports.buildStandardsContext = buildStandardsContext;
903
+ exports.buildStandardsContextBlock = buildStandardsContextBlock;
904
+ exports.evaluateStandardsCoverage = evaluateStandardsCoverage;
905
+ exports.lintFrameworkPack = lintFrameworkPack;
906
+ exports.resolveStandardsPacks = resolveStandardsPacks;
907
+ exports.selectStatementsForLesson = selectStatementsForLesson;
908
+ exports.validateFrameworkPack = validateFrameworkPack;
909
+ //# sourceMappingURL=index.cjs.map
910
+ //# sourceMappingURL=index.cjs.map