agent-cms 0.1.0 → 0.3.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.
- package/README.md +113 -2
- package/dist/codemode-handler-Bgu2utjN.mjs +18697 -0
- package/dist/{handler-ClOW1ldA.mjs → handler-B5jgfPOY.mjs} +2 -2
- package/dist/http-transport-DVKhbbe1.mjs +17 -0
- package/dist/index.d.mts +15 -0
- package/dist/index.mjs +431 -34
- package/dist/{token-service-BDjccMmz.mjs → preview-service-C9Tmhdye.mjs} +177 -38
- package/dist/{http-transport-DbFCI6Cs.mjs → server-D0XqvDjU.mjs} +200 -241
- package/dist/{structured-text-service-B4xSlUg_.mjs → structured-text-service-BJkqWRkq.mjs} +187 -8
- package/migrations/0000_genesis.sql +11 -0
- package/package.json +5 -5
|
@@ -617,7 +617,7 @@ function isContentRow(row) {
|
|
|
617
617
|
return typeof row === "object" && row !== null && "id" in row && "_status" in row;
|
|
618
618
|
}
|
|
619
619
|
/** Runtime check that a value is a plain object record */
|
|
620
|
-
function isRecord(value) {
|
|
620
|
+
function isRecord$1(value) {
|
|
621
621
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
622
622
|
}
|
|
623
623
|
/** Safely parse JSON to a Record, returning empty object on failure */
|
|
@@ -625,7 +625,7 @@ function parseJsonRecord(json) {
|
|
|
625
625
|
if (!json) return {};
|
|
626
626
|
try {
|
|
627
627
|
const parsed = JSON.parse(json);
|
|
628
|
-
if (isRecord(parsed)) return parsed;
|
|
628
|
+
if (isRecord$1(parsed)) return parsed;
|
|
629
629
|
} catch {}
|
|
630
630
|
return {};
|
|
631
631
|
}
|
|
@@ -1287,6 +1287,185 @@ function markdownToDast(markdown) {
|
|
|
1287
1287
|
});
|
|
1288
1288
|
}
|
|
1289
1289
|
//#endregion
|
|
1290
|
+
//#region src/dast/expand-shorthand.ts
|
|
1291
|
+
/**
|
|
1292
|
+
* Expand structured_text shorthand formats into the canonical
|
|
1293
|
+
* { value: DastDocument, blocks: Record<string, unknown> } shape.
|
|
1294
|
+
*
|
|
1295
|
+
* Accepted input formats:
|
|
1296
|
+
* 1. String → markdown, converted via markdownToDast
|
|
1297
|
+
* 2. Array → typed nodes, converted to DAST children
|
|
1298
|
+
* 3. Object with "markdown" key → markdown + optional blocks
|
|
1299
|
+
* 4. Object with "nodes" key → typed nodes + optional blocks
|
|
1300
|
+
* 5. Object with "value" key containing { schema: "dast" } → pass through (canonical)
|
|
1301
|
+
* 6. Any other object → pass through unchanged
|
|
1302
|
+
*/
|
|
1303
|
+
/**
|
|
1304
|
+
* Parse a text string with inline markdown into DAST inline (span) nodes.
|
|
1305
|
+
* Returns the children of the first paragraph, or a single span fallback.
|
|
1306
|
+
*/
|
|
1307
|
+
function parseInlineSpans(text) {
|
|
1308
|
+
const first = markdownToDast(text).document.children.at(0);
|
|
1309
|
+
if (first != null && "children" in first) return first.children;
|
|
1310
|
+
return [{
|
|
1311
|
+
type: "span",
|
|
1312
|
+
value: text
|
|
1313
|
+
}];
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* Convert an array of typed node objects to DAST block-level children.
|
|
1317
|
+
*/
|
|
1318
|
+
function typedNodesToDastChildren(nodes) {
|
|
1319
|
+
const children = [];
|
|
1320
|
+
for (const node of nodes) {
|
|
1321
|
+
if (node == null || typeof node !== "object") continue;
|
|
1322
|
+
const n = node;
|
|
1323
|
+
switch (n.type) {
|
|
1324
|
+
case "paragraph":
|
|
1325
|
+
children.push({
|
|
1326
|
+
type: "paragraph",
|
|
1327
|
+
children: parseInlineSpans(String(n.text ?? ""))
|
|
1328
|
+
});
|
|
1329
|
+
break;
|
|
1330
|
+
case "heading":
|
|
1331
|
+
children.push({
|
|
1332
|
+
type: "heading",
|
|
1333
|
+
level: n.level,
|
|
1334
|
+
children: parseInlineSpans(String(n.text ?? ""))
|
|
1335
|
+
});
|
|
1336
|
+
break;
|
|
1337
|
+
case "code":
|
|
1338
|
+
children.push({
|
|
1339
|
+
type: "code",
|
|
1340
|
+
code: n.code,
|
|
1341
|
+
...n.language ? { language: n.language } : {}
|
|
1342
|
+
});
|
|
1343
|
+
break;
|
|
1344
|
+
case "blockquote":
|
|
1345
|
+
children.push({
|
|
1346
|
+
type: "blockquote",
|
|
1347
|
+
children: [{
|
|
1348
|
+
type: "paragraph",
|
|
1349
|
+
children: parseInlineSpans(String(n.text ?? ""))
|
|
1350
|
+
}]
|
|
1351
|
+
});
|
|
1352
|
+
break;
|
|
1353
|
+
case "list":
|
|
1354
|
+
children.push({
|
|
1355
|
+
type: "list",
|
|
1356
|
+
style: n.style ?? "bulleted",
|
|
1357
|
+
children: (Array.isArray(n.items) ? n.items : []).map((item) => ({
|
|
1358
|
+
type: "listItem",
|
|
1359
|
+
children: [{
|
|
1360
|
+
type: "paragraph",
|
|
1361
|
+
children: parseInlineSpans(String(item ?? ""))
|
|
1362
|
+
}]
|
|
1363
|
+
}))
|
|
1364
|
+
});
|
|
1365
|
+
break;
|
|
1366
|
+
case "thematicBreak":
|
|
1367
|
+
children.push({ type: "thematicBreak" });
|
|
1368
|
+
break;
|
|
1369
|
+
case "block":
|
|
1370
|
+
children.push({
|
|
1371
|
+
type: "block",
|
|
1372
|
+
item: n.ref
|
|
1373
|
+
});
|
|
1374
|
+
break;
|
|
1375
|
+
default: break;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
return children;
|
|
1379
|
+
}
|
|
1380
|
+
/**
|
|
1381
|
+
* Build a block map from an array of block entries.
|
|
1382
|
+
*
|
|
1383
|
+
* Accepts two entry formats:
|
|
1384
|
+
* - { id, type, data: { ...fields } } — shorthand (type becomes _type)
|
|
1385
|
+
* - { id, _type, ...fields } — canonical (matches get_record output)
|
|
1386
|
+
*/
|
|
1387
|
+
function buildBlockMapFromArray(blocks) {
|
|
1388
|
+
const map = {};
|
|
1389
|
+
for (const b of blocks) {
|
|
1390
|
+
if (b == null || typeof b !== "object") continue;
|
|
1391
|
+
const entry = b;
|
|
1392
|
+
const id = entry.id;
|
|
1393
|
+
if (typeof id !== "string") continue;
|
|
1394
|
+
if (typeof entry._type === "string") {
|
|
1395
|
+
const { id: _, ...rest } = entry;
|
|
1396
|
+
map[id] = rest;
|
|
1397
|
+
continue;
|
|
1398
|
+
}
|
|
1399
|
+
const type = entry.type;
|
|
1400
|
+
if (typeof type === "string") {
|
|
1401
|
+
const data = entry.data;
|
|
1402
|
+
map[id] = {
|
|
1403
|
+
_type: type,
|
|
1404
|
+
...data != null && typeof data === "object" && !Array.isArray(data) ? data : {}
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
return map;
|
|
1409
|
+
}
|
|
1410
|
+
/**
|
|
1411
|
+
* Normalize a blocks value to a canonical map.
|
|
1412
|
+
*
|
|
1413
|
+
* Accepts:
|
|
1414
|
+
* - Array of block entries (shorthand or canonical format)
|
|
1415
|
+
* - Object/map keyed by block ID (canonical DAST format, passed through)
|
|
1416
|
+
*/
|
|
1417
|
+
function normalizeBlocks(blocks) {
|
|
1418
|
+
if (Array.isArray(blocks)) return buildBlockMapFromArray(blocks);
|
|
1419
|
+
if (blocks != null && typeof blocks === "object" && !Array.isArray(blocks)) return blocks;
|
|
1420
|
+
return {};
|
|
1421
|
+
}
|
|
1422
|
+
function isRecord(v) {
|
|
1423
|
+
return v != null && typeof v === "object" && !Array.isArray(v);
|
|
1424
|
+
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Expand a structured_text field value from shorthand formats to canonical form.
|
|
1427
|
+
*
|
|
1428
|
+
* Returns the value unchanged if it is already in canonical form or unrecognized.
|
|
1429
|
+
* For shorthand formats (string, array, or wrapper objects), returns the expanded
|
|
1430
|
+
* { value: DastDocument, blocks: Record<string, unknown> } shape.
|
|
1431
|
+
*/
|
|
1432
|
+
function expandStructuredTextShorthand(rawValue) {
|
|
1433
|
+
if (typeof rawValue === "string") return {
|
|
1434
|
+
value: markdownToDast(rawValue),
|
|
1435
|
+
blocks: {}
|
|
1436
|
+
};
|
|
1437
|
+
if (Array.isArray(rawValue)) return {
|
|
1438
|
+
value: {
|
|
1439
|
+
schema: "dast",
|
|
1440
|
+
document: {
|
|
1441
|
+
type: "root",
|
|
1442
|
+
children: typedNodesToDastChildren(rawValue)
|
|
1443
|
+
}
|
|
1444
|
+
},
|
|
1445
|
+
blocks: {}
|
|
1446
|
+
};
|
|
1447
|
+
if (!isRecord(rawValue)) return rawValue;
|
|
1448
|
+
if ("markdown" in rawValue && typeof rawValue.markdown === "string") return {
|
|
1449
|
+
value: markdownToDast(rawValue.markdown),
|
|
1450
|
+
blocks: normalizeBlocks(rawValue.blocks)
|
|
1451
|
+
};
|
|
1452
|
+
if ("nodes" in rawValue && Array.isArray(rawValue.nodes)) {
|
|
1453
|
+
const children = typedNodesToDastChildren(rawValue.nodes);
|
|
1454
|
+
const blocks = normalizeBlocks(rawValue.blocks);
|
|
1455
|
+
return {
|
|
1456
|
+
value: {
|
|
1457
|
+
schema: "dast",
|
|
1458
|
+
document: {
|
|
1459
|
+
type: "root",
|
|
1460
|
+
children
|
|
1461
|
+
}
|
|
1462
|
+
},
|
|
1463
|
+
blocks
|
|
1464
|
+
};
|
|
1465
|
+
}
|
|
1466
|
+
return rawValue;
|
|
1467
|
+
}
|
|
1468
|
+
//#endregion
|
|
1290
1469
|
//#region src/graphql/sql-metrics.ts
|
|
1291
1470
|
const sqlMetricsStorage = new AsyncLocalStorage();
|
|
1292
1471
|
function withSqlMetrics(run) {
|
|
@@ -1573,11 +1752,11 @@ function compileStructuredText(ctx, container, params) {
|
|
|
1573
1752
|
for (const field of blockModel.fields) {
|
|
1574
1753
|
const value = blockData[field.api_key];
|
|
1575
1754
|
if (value === void 0) continue;
|
|
1755
|
+
if (value === null) {
|
|
1756
|
+
row[field.api_key] = null;
|
|
1757
|
+
continue;
|
|
1758
|
+
}
|
|
1576
1759
|
if (field.field_type === "structured_text") {
|
|
1577
|
-
if (value === null) {
|
|
1578
|
-
row[field.api_key] = null;
|
|
1579
|
-
continue;
|
|
1580
|
-
}
|
|
1581
1760
|
const nestedInput = yield* decodeStructuredTextInput(field.api_key, value);
|
|
1582
1761
|
const nestedCompiled = yield* compileStructuredText(ctx, {
|
|
1583
1762
|
parentContainerModelApiKey: blockModel.apiKey,
|
|
@@ -1947,6 +2126,6 @@ function materializeRecordStructuredTextFields(params) {
|
|
|
1947
2126
|
});
|
|
1948
2127
|
}
|
|
1949
2128
|
//#endregion
|
|
1950
|
-
export { isSearchable as A, encodeJson as B, findUniqueConstraintViolations as C, getLinksTargets as D, getLinkTargets as E, parseMediaGalleryReferences as F, ReferenceConflictError as G, getFieldTypeDef as H, decodeJsonIfString as I, ValidationError as J, SchemaEngineError as K, decodeJsonRecordStringOr as L, supportsUniqueValidation as M, mergeAssetWithMediaReference as N, getSlugSource as O, parseMediaFieldReference as P, decodeJsonString as R, computeIsValid as S, getBlocksOnly as T, DuplicateError as U, FIELD_TYPE_REGISTRY as V, NotFoundError as W, isCmsError as X, errorToResponse as Y, extractLinkIds as _, materializeStructuredTextValue as a, isContentRow as b, FIELD_TYPES as c, getSqlMetrics as d, recordSqlMetrics as f, extractInlineBlockIds as g, extractBlockIds as h, materializeRecordStructuredTextFields as i, isUnique as j, isRequired as k, isFieldType as l,
|
|
2129
|
+
export { isSearchable as A, encodeJson as B, findUniqueConstraintViolations as C, getLinksTargets as D, getLinkTargets as E, parseMediaGalleryReferences as F, ReferenceConflictError as G, getFieldTypeDef as H, decodeJsonIfString as I, ValidationError as J, SchemaEngineError as K, decodeJsonRecordStringOr as L, supportsUniqueValidation as M, mergeAssetWithMediaReference as N, getSlugSource as O, parseMediaFieldReference as P, decodeJsonString as R, computeIsValid as S, getBlocksOnly as T, DuplicateError as U, FIELD_TYPE_REGISTRY as V, NotFoundError as W, isCmsError as X, errorToResponse as Y, extractLinkIds as _, materializeStructuredTextValue as a, isContentRow as b, FIELD_TYPES as c, getSqlMetrics as d, recordSqlMetrics as f, extractInlineBlockIds as g, extractBlockIds as h, materializeRecordStructuredTextFields as i, isUnique as j, isRequired as k, isFieldType as l, expandStructuredTextShorthand as m, deleteBlocksForField as n, materializeStructuredTextValues as o, withSqlMetrics as p, UnauthorizedError as q, getStructuredTextStorageKey as r, writeStructuredText as s, deleteBlockSubtrees as t, runBatchedQueries as u, pruneBlockNodes as v, getBlockWhitelist as w, parseFieldValidators as x, StructuredTextWriteInput as y, decodeJsonStringOr as z };
|
|
1951
2130
|
|
|
1952
|
-
//# sourceMappingURL=structured-text-service-
|
|
2131
|
+
//# sourceMappingURL=structured-text-service-BJkqWRkq.mjs.map
|
|
@@ -32,6 +32,7 @@ CREATE TABLE IF NOT EXISTS "models" (
|
|
|
32
32
|
"has_draft" integer DEFAULT true NOT NULL,
|
|
33
33
|
"all_locales_required" integer DEFAULT 0 NOT NULL,
|
|
34
34
|
"ordering" text,
|
|
35
|
+
"canonical_path_template" text,
|
|
35
36
|
"created_at" text NOT NULL,
|
|
36
37
|
"updated_at" text NOT NULL
|
|
37
38
|
);
|
|
@@ -116,3 +117,13 @@ CREATE TABLE IF NOT EXISTS "editor_tokens" (
|
|
|
116
117
|
|
|
117
118
|
CREATE UNIQUE INDEX IF NOT EXISTS "idx_editor_tokens_secret_hash"
|
|
118
119
|
ON "editor_tokens" ("secret_hash");
|
|
120
|
+
|
|
121
|
+
CREATE TABLE IF NOT EXISTS "preview_tokens" (
|
|
122
|
+
"id" text PRIMARY KEY,
|
|
123
|
+
"token_hash" text NOT NULL UNIQUE,
|
|
124
|
+
"expires_at" text NOT NULL,
|
|
125
|
+
"created_at" text NOT NULL DEFAULT (datetime('now'))
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
CREATE INDEX IF NOT EXISTS "idx_preview_tokens_hash"
|
|
129
|
+
ON "preview_tokens" ("token_hash");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-cms",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Agent-first headless CMS for Cloudflare Workers. Schema, content, and assets via MCP. GraphQL delivery.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@10.32.1",
|
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
],
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "tsdown",
|
|
25
|
-
"dev": "wrangler dev",
|
|
26
25
|
"test": "vitest",
|
|
27
26
|
"test:run": "vitest run",
|
|
28
27
|
"lint": "oxlint --type-check src/",
|
|
@@ -30,7 +29,6 @@
|
|
|
30
29
|
"bench:blog": "node scripts/bench-blog.mjs",
|
|
31
30
|
"dato:import": "node scripts/dato-import/cli.mjs",
|
|
32
31
|
"prepublishOnly": "pnpm run build",
|
|
33
|
-
"db:migrate": "wrangler d1 migrations apply agent-cms-db --local",
|
|
34
32
|
"prepare": "effect-language-service patch"
|
|
35
33
|
},
|
|
36
34
|
"keywords": [
|
|
@@ -59,6 +57,7 @@
|
|
|
59
57
|
"dependencies": {
|
|
60
58
|
"@aws-sdk/client-s3": "^3.1014.0",
|
|
61
59
|
"@aws-sdk/s3-request-presigner": "^3.1014.0",
|
|
60
|
+
"@cloudflare/codemode": "^0.3.2",
|
|
62
61
|
"@effect/ai": "^0.35.0",
|
|
63
62
|
"@effect/cli": "^0.75.0",
|
|
64
63
|
"@effect/experimental": "^0.60.0",
|
|
@@ -69,13 +68,14 @@
|
|
|
69
68
|
"@effect/rpc": "^0.75.0",
|
|
70
69
|
"@effect/sql": "^0.51.0",
|
|
71
70
|
"@effect/sql-d1": "^0.49.0",
|
|
71
|
+
"agents": "^0.8.6",
|
|
72
72
|
"effect": "^3.21.0",
|
|
73
73
|
"graphql": "^16.13.1",
|
|
74
74
|
"graphql-yoga": "^5.18.1",
|
|
75
|
+
"nanoid": "^5.1.7",
|
|
75
76
|
"remark-gfm": "^4.0.1",
|
|
76
77
|
"remark-parse": "^11.0.0",
|
|
77
78
|
"remark-stringify": "^11.0.0",
|
|
78
|
-
"nanoid": "^5.1.7",
|
|
79
79
|
"slugify": "^1.6.8",
|
|
80
80
|
"unified": "^11.0.5"
|
|
81
81
|
},
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"@cloudflare/workers-types": "^4.20260317.1",
|
|
84
84
|
"@effect/language-service": "^0.81.0",
|
|
85
85
|
"@effect/sql-sqlite-node": "^0.52.0",
|
|
86
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
86
|
+
"@modelcontextprotocol/sdk": "^1.28.0",
|
|
87
87
|
"@types/better-sqlite3": "^7.6.13",
|
|
88
88
|
"@types/mdast": "^4.0.4",
|
|
89
89
|
"better-sqlite3": "^12.8.0",
|