zam-core 0.6.1 → 0.6.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/dist/cli/index.js +3187 -132
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +13 -0
- package/dist/index.js +93 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -806,6 +806,8 @@ interface Token {
|
|
|
806
806
|
created_at: string;
|
|
807
807
|
updated_at: string;
|
|
808
808
|
deprecated_at: string | null;
|
|
809
|
+
provider: string | null;
|
|
810
|
+
topic_id: string | null;
|
|
809
811
|
}
|
|
810
812
|
interface CreateTokenInput {
|
|
811
813
|
slug: string;
|
|
@@ -816,6 +818,8 @@ interface CreateTokenInput {
|
|
|
816
818
|
symbiosis_mode?: SymbiosisMode | null;
|
|
817
819
|
source_link?: string | null;
|
|
818
820
|
question?: string | null;
|
|
821
|
+
provider?: string | null;
|
|
822
|
+
topic_id?: string | null;
|
|
819
823
|
}
|
|
820
824
|
interface UpdateTokenInput {
|
|
821
825
|
concept?: string;
|
|
@@ -825,6 +829,8 @@ interface UpdateTokenInput {
|
|
|
825
829
|
symbiosis_mode?: SymbiosisMode | null;
|
|
826
830
|
source_link?: string | null;
|
|
827
831
|
question?: string | null;
|
|
832
|
+
provider?: string | null;
|
|
833
|
+
topic_id?: string | null;
|
|
828
834
|
}
|
|
829
835
|
interface ListTokensOptions {
|
|
830
836
|
domain?: string;
|
|
@@ -922,6 +928,8 @@ interface PersonalCard {
|
|
|
922
928
|
elapsedDays: number | null;
|
|
923
929
|
scheduledDays: number | null;
|
|
924
930
|
blocked: number | null;
|
|
931
|
+
provider: string | null;
|
|
932
|
+
topicId: string | null;
|
|
925
933
|
}
|
|
926
934
|
declare function slugify(text: string): string;
|
|
927
935
|
declare function generateTokenSlug(db: Database, domain: string, concept: string, question?: string | null): Promise<string>;
|
|
@@ -937,6 +945,8 @@ interface CurriculumCardInput {
|
|
|
937
945
|
context?: string;
|
|
938
946
|
bloom_level?: number;
|
|
939
947
|
symbiosis_mode?: string | null;
|
|
948
|
+
provider?: string | null;
|
|
949
|
+
topic_id?: string | null;
|
|
940
950
|
}
|
|
941
951
|
interface ImportCurriculumResult {
|
|
942
952
|
createdCount: number;
|
|
@@ -990,6 +1000,9 @@ interface SourceProposalInput {
|
|
|
990
1000
|
symbiosis_mode: string;
|
|
991
1001
|
excerpt: string;
|
|
992
1002
|
page_number?: string | null;
|
|
1003
|
+
provider?: string | null;
|
|
1004
|
+
topic_id?: string | null;
|
|
1005
|
+
source_id?: string | null;
|
|
993
1006
|
}
|
|
994
1007
|
/**
|
|
995
1008
|
* Confirm source import transaction.
|
package/dist/index.js
CHANGED
|
@@ -539,7 +539,9 @@ CREATE TABLE IF NOT EXISTS tokens (
|
|
|
539
539
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
540
540
|
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
541
541
|
deprecated_at TEXT,
|
|
542
|
-
question TEXT
|
|
542
|
+
question TEXT,
|
|
543
|
+
provider TEXT,
|
|
544
|
+
topic_id TEXT
|
|
543
545
|
);
|
|
544
546
|
|
|
545
547
|
-- Prerequisite dependency graph: "to learn A, first know B"
|
|
@@ -979,6 +981,14 @@ async function runMigrations(db) {
|
|
|
979
981
|
PRIMARY KEY (token_id, source_id)
|
|
980
982
|
)
|
|
981
983
|
`);
|
|
984
|
+
if (tokenCols.length > 0) {
|
|
985
|
+
if (!tokenCols.some((c) => c.name === "provider")) {
|
|
986
|
+
await db.exec(`ALTER TABLE tokens ADD COLUMN provider TEXT`);
|
|
987
|
+
}
|
|
988
|
+
if (!tokenCols.some((c) => c.name === "topic_id")) {
|
|
989
|
+
await db.exec(`ALTER TABLE tokens ADD COLUMN topic_id TEXT`);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
982
992
|
}
|
|
983
993
|
|
|
984
994
|
// src/kernel/db/snapshot.ts
|
|
@@ -1481,8 +1491,8 @@ async function createToken(db, input) {
|
|
|
1481
1491
|
throw new Error(`bloom_level must be between 1 and 5, got ${bloom}`);
|
|
1482
1492
|
}
|
|
1483
1493
|
await db.prepare(`
|
|
1484
|
-
INSERT INTO tokens (id, slug, concept, domain, bloom_level, context, symbiosis_mode, source_link, question, created_at, updated_at)
|
|
1485
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1494
|
+
INSERT INTO tokens (id, slug, concept, domain, bloom_level, context, symbiosis_mode, source_link, question, provider, topic_id, created_at, updated_at)
|
|
1495
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1486
1496
|
`).run(
|
|
1487
1497
|
id,
|
|
1488
1498
|
input.slug,
|
|
@@ -1493,16 +1503,33 @@ async function createToken(db, input) {
|
|
|
1493
1503
|
input.symbiosis_mode ?? null,
|
|
1494
1504
|
input.source_link ?? null,
|
|
1495
1505
|
input.question ?? null,
|
|
1506
|
+
input.provider ?? null,
|
|
1507
|
+
input.topic_id ?? null,
|
|
1496
1508
|
now,
|
|
1497
1509
|
now
|
|
1498
1510
|
);
|
|
1499
1511
|
return await getTokenById(db, id);
|
|
1500
1512
|
}
|
|
1513
|
+
function parseTokenFallback(token) {
|
|
1514
|
+
if (token && !token.provider && token.source_link) {
|
|
1515
|
+
if (token.source_link.includes("lehrplanplus.bayern.de")) {
|
|
1516
|
+
token.provider = "lehrplanplus-bayern";
|
|
1517
|
+
const match = token.source_link.match(/#(.*)$/);
|
|
1518
|
+
if (match) {
|
|
1519
|
+
token.topic_id = match[1];
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1501
1524
|
async function getTokenBySlug(db, slug) {
|
|
1502
|
-
|
|
1525
|
+
const token = await db.prepare("SELECT * FROM tokens WHERE slug = ?").get(slug);
|
|
1526
|
+
parseTokenFallback(token);
|
|
1527
|
+
return token;
|
|
1503
1528
|
}
|
|
1504
1529
|
async function getTokenById(db, id) {
|
|
1505
|
-
|
|
1530
|
+
const token = await db.prepare("SELECT * FROM tokens WHERE id = ?").get(id);
|
|
1531
|
+
parseTokenFallback(token);
|
|
1532
|
+
return token;
|
|
1506
1533
|
}
|
|
1507
1534
|
async function updateToken(db, slug, updates) {
|
|
1508
1535
|
const token = await getTokenBySlug(db, slug);
|
|
@@ -1548,6 +1575,14 @@ async function updateToken(db, slug, updates) {
|
|
|
1548
1575
|
fields.push("question = ?");
|
|
1549
1576
|
values.push(updates.question);
|
|
1550
1577
|
}
|
|
1578
|
+
if (updates.provider !== void 0) {
|
|
1579
|
+
fields.push("provider = ?");
|
|
1580
|
+
values.push(updates.provider);
|
|
1581
|
+
}
|
|
1582
|
+
if (updates.topic_id !== void 0) {
|
|
1583
|
+
fields.push("topic_id = ?");
|
|
1584
|
+
values.push(updates.topic_id);
|
|
1585
|
+
}
|
|
1551
1586
|
if (fields.length === 0) {
|
|
1552
1587
|
throw new Error("updateToken called with no fields to update");
|
|
1553
1588
|
}
|
|
@@ -1673,14 +1708,20 @@ async function findTokens(db, query) {
|
|
|
1673
1708
|
return scored;
|
|
1674
1709
|
}
|
|
1675
1710
|
async function listTokens(db, options) {
|
|
1711
|
+
let tokens;
|
|
1676
1712
|
if (options?.domain) {
|
|
1677
|
-
|
|
1713
|
+
tokens = await db.prepare(
|
|
1678
1714
|
"SELECT * FROM tokens WHERE domain = ? AND deprecated_at IS NULL ORDER BY bloom_level, slug"
|
|
1679
1715
|
).all(options.domain);
|
|
1716
|
+
} else {
|
|
1717
|
+
tokens = await db.prepare(
|
|
1718
|
+
"SELECT * FROM tokens WHERE deprecated_at IS NULL ORDER BY bloom_level, domain, slug"
|
|
1719
|
+
).all();
|
|
1680
1720
|
}
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1721
|
+
for (const token of tokens) {
|
|
1722
|
+
parseTokenFallback(token);
|
|
1723
|
+
}
|
|
1724
|
+
return tokens;
|
|
1684
1725
|
}
|
|
1685
1726
|
function slugify(text) {
|
|
1686
1727
|
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
@@ -1732,6 +1773,8 @@ async function listPersonalCards(db, userId, options) {
|
|
|
1732
1773
|
t.question,
|
|
1733
1774
|
t.created_at AS createdAt,
|
|
1734
1775
|
t.updated_at AS updatedAt,
|
|
1776
|
+
t.provider,
|
|
1777
|
+
t.topic_id AS topicId,
|
|
1735
1778
|
c.id AS cardId,
|
|
1736
1779
|
c.state,
|
|
1737
1780
|
c.due_at AS dueAt,
|
|
@@ -1761,6 +1804,17 @@ async function listPersonalCards(db, userId, options) {
|
|
|
1761
1804
|
}
|
|
1762
1805
|
sql += " ORDER BY t.created_at DESC";
|
|
1763
1806
|
const rows = await db.prepare(sql).all(...values);
|
|
1807
|
+
for (const row of rows) {
|
|
1808
|
+
if (!row.provider && row.sourceLink) {
|
|
1809
|
+
if (row.sourceLink.includes("lehrplanplus.bayern.de")) {
|
|
1810
|
+
row.provider = "lehrplanplus-bayern";
|
|
1811
|
+
const match = row.sourceLink.match(/#(.*)$/);
|
|
1812
|
+
if (match) {
|
|
1813
|
+
row.topicId = match[1];
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1764
1818
|
return rows;
|
|
1765
1819
|
}
|
|
1766
1820
|
async function importCurriculumCards(db, userId, cards) {
|
|
@@ -1807,9 +1861,18 @@ async function importCurriculumCards(db, userId, cards) {
|
|
|
1807
1861
|
context: card.context || "",
|
|
1808
1862
|
symbiosis_mode: symbiosisMode,
|
|
1809
1863
|
source_link: card.source_link || null,
|
|
1810
|
-
question: card.question || null
|
|
1864
|
+
question: card.question || null,
|
|
1865
|
+
provider: card.provider || null,
|
|
1866
|
+
topic_id: card.topic_id || null
|
|
1811
1867
|
});
|
|
1812
1868
|
createdCount++;
|
|
1869
|
+
} else {
|
|
1870
|
+
if (!token.provider && card.provider) {
|
|
1871
|
+
await updateToken(tx, token.slug, {
|
|
1872
|
+
provider: card.provider,
|
|
1873
|
+
topic_id: card.topic_id || null
|
|
1874
|
+
});
|
|
1875
|
+
}
|
|
1813
1876
|
}
|
|
1814
1877
|
const existingCard = await getCard(tx, token.id, userId);
|
|
1815
1878
|
if (!existingCard) {
|
|
@@ -2031,11 +2094,12 @@ async function confirmSourceImport(db, userId, sourceId, proposals) {
|
|
|
2031
2094
|
let createdCount = 0;
|
|
2032
2095
|
let linkedCount = 0;
|
|
2033
2096
|
await db.transaction(async (tx) => {
|
|
2034
|
-
const source = await tx.prepare("SELECT id FROM sources WHERE id = ?").get(sourceId);
|
|
2035
|
-
if (!source) {
|
|
2036
|
-
throw new Error(`Source not found: ${sourceId}`);
|
|
2037
|
-
}
|
|
2038
2097
|
for (const card of proposals) {
|
|
2098
|
+
const cardSourceId = card.source_id || sourceId;
|
|
2099
|
+
const source = await tx.prepare("SELECT id FROM sources WHERE id = ?").get(cardSourceId);
|
|
2100
|
+
if (!source) {
|
|
2101
|
+
throw new Error(`Source not found: ${cardSourceId}`);
|
|
2102
|
+
}
|
|
2039
2103
|
const baseText = card.question && card.question.trim().length > 0 ? card.question : card.concept;
|
|
2040
2104
|
const cleanDomain = slugify(card.domain || "");
|
|
2041
2105
|
const cleanBase = slugify(baseText);
|
|
@@ -2066,11 +2130,19 @@ async function confirmSourceImport(db, userId, sourceId, proposals) {
|
|
|
2066
2130
|
bloom_level: bloom,
|
|
2067
2131
|
context: card.excerpt || "",
|
|
2068
2132
|
symbiosis_mode: symbiosisMode,
|
|
2069
|
-
question: card.question || null
|
|
2133
|
+
question: card.question || null,
|
|
2134
|
+
provider: card.provider || null,
|
|
2135
|
+
topic_id: card.topic_id || null
|
|
2070
2136
|
});
|
|
2071
2137
|
createdCount++;
|
|
2072
2138
|
} else {
|
|
2073
2139
|
linkedCount++;
|
|
2140
|
+
if (!token.provider && card.provider) {
|
|
2141
|
+
await updateToken(tx, token.slug, {
|
|
2142
|
+
provider: card.provider,
|
|
2143
|
+
topic_id: card.topic_id || null
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
2074
2146
|
}
|
|
2075
2147
|
const existingCard = await getCard(tx, token.id, userId);
|
|
2076
2148
|
if (!existingCard) {
|
|
@@ -2082,7 +2154,12 @@ async function confirmSourceImport(db, userId, sourceId, proposals) {
|
|
|
2082
2154
|
ON CONFLICT(token_id, source_id) DO UPDATE SET
|
|
2083
2155
|
excerpt = excluded.excerpt,
|
|
2084
2156
|
page_number = excluded.page_number`
|
|
2085
|
-
).run(
|
|
2157
|
+
).run(
|
|
2158
|
+
token.id,
|
|
2159
|
+
cardSourceId,
|
|
2160
|
+
card.excerpt || "",
|
|
2161
|
+
card.page_number || null
|
|
2162
|
+
);
|
|
2086
2163
|
}
|
|
2087
2164
|
});
|
|
2088
2165
|
return { createdCount, linkedCount };
|