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
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,183 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
import { J as ValidationError, X as isCmsError, Y as errorToResponse, q as UnauthorizedError } from "./structured-text-service-
|
|
1
|
+
import { $ as bulkCreateRecords, A as clearSchedule, C as ReorderInput, Ct as search, D as UpdateAssetMetadataInput, E as SearchInput, F as deleteLocale, G as listAssets, H as deleteAsset, I as listLocales, J as updateAssetMetadata, K as replaceAsset, M as schedulePublish, N as scheduleUnpublish, O as UpdateFieldInput, P as createLocale, Q as unpublishRecord, S as ReindexSearchInput, St as reindexAll, T as SearchAssetsInput, U as getAsset, V as createAsset, Z as publishRecord, _ as CreateUploadUrlInput, _t as deleteModel, a as listEditorTokens, at as removeRecord, b as PatchBlocksInput, bt as listModels, c as exportSchema, ct as getVersion, d as CreateAssetInput, dt as HooksContext, et as createRecord, f as CreateEditorTokenInput, ft as createField, g as CreateRecordInput, gt as createModel, h as CreateModelInput, ht as updateField, i as createEditorToken, it as patchRecord, j as runScheduledTransitions, k as UpdateModelInput, l as importSchema, lt as listVersions, m as CreateLocaleInput, mt as listFields, nt as listRecords, o as revokeEditorToken, ot as reorderRecords, p as CreateFieldInput, pt as deleteField, q as searchAssets, r as validatePreviewToken, rt as patchBlocksForField, s as validateEditorToken, t as createPreviewToken, tt as getRecord, u as BulkCreateRecordsInput, ut as restoreVersion, vt as getModel, w as ScheduleRecordInput, wt as VectorizeContext, x as PatchRecordInput, xt as updateModel, y as ImportSchemaInput } from "./preview-service-C9Tmhdye.mjs";
|
|
2
|
+
import { E as getLinkTargets, J as ValidationError, L as decodeJsonRecordStringOr, W as NotFoundError, X as isCmsError, Y as errorToResponse, q as UnauthorizedError } from "./structured-text-service-BJkqWRkq.mjs";
|
|
3
3
|
import { D1Client } from "@effect/sql-d1";
|
|
4
4
|
import { Cause, Effect, Layer, Logger, Option, ParseResult, Schema } from "effect";
|
|
5
|
-
import { HttpApp, HttpRouter, HttpServerError, HttpServerRequest, HttpServerResponse } from "@effect/platform";
|
|
5
|
+
import { HttpApi, HttpApiEndpoint, HttpApiGroup, HttpApp, HttpRouter, HttpServerError, HttpServerRequest, HttpServerResponse, OpenApi } from "@effect/platform";
|
|
6
6
|
import { SqlClient } from "@effect/sql";
|
|
7
|
+
//#region src/services/path-service.ts
|
|
8
|
+
/**
|
|
9
|
+
* Canonical path resolver — batch-resolves canonical_path_template for all
|
|
10
|
+
* published records of a model, traversing link fields via dot notation.
|
|
11
|
+
*
|
|
12
|
+
* Template examples:
|
|
13
|
+
* /blog/{slug} — flat field
|
|
14
|
+
* /blog/{category.slug}/{slug} — one-hop link traversal
|
|
15
|
+
* /{category.parent.slug}/{category.slug}/{slug} — multi-hop
|
|
16
|
+
*
|
|
17
|
+
* Unresolvable tokens (null link, missing field) are left as-is: {token}.
|
|
18
|
+
*/
|
|
19
|
+
const MAX_DEPTH = 10;
|
|
20
|
+
function parseTemplateTokens(template) {
|
|
21
|
+
const tokens = [];
|
|
22
|
+
const re = /\{([^}]+)\}/g;
|
|
23
|
+
let match;
|
|
24
|
+
while ((match = re.exec(template)) !== null) tokens.push({
|
|
25
|
+
raw: match[1],
|
|
26
|
+
segments: match[1].split(".")
|
|
27
|
+
});
|
|
28
|
+
return tokens;
|
|
29
|
+
}
|
|
30
|
+
function parseValidators(raw) {
|
|
31
|
+
if (!raw || raw === "") return {};
|
|
32
|
+
return decodeJsonRecordStringOr(raw, {});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolve canonical paths for all published records of a model.
|
|
36
|
+
* Returns array of { id, path, lastmod }.
|
|
37
|
+
*/
|
|
38
|
+
function resolveCanonicalPaths(modelApiKey) {
|
|
39
|
+
return Effect.gen(function* () {
|
|
40
|
+
const sql = yield* SqlClient.SqlClient;
|
|
41
|
+
const models = yield* sql.unsafe("SELECT * FROM models WHERE api_key = ?", [modelApiKey]);
|
|
42
|
+
if (models.length === 0) return yield* new NotFoundError({
|
|
43
|
+
entity: "Model",
|
|
44
|
+
id: modelApiKey
|
|
45
|
+
});
|
|
46
|
+
const model = models[0];
|
|
47
|
+
const template = model.canonical_path_template;
|
|
48
|
+
if (!template) return yield* new ValidationError({ message: `Model "${modelApiKey}" has no canonical_path_template. Set one via update_model before resolving paths.` });
|
|
49
|
+
const tokens = parseTemplateTokens(template);
|
|
50
|
+
if (tokens.length === 0) return yield* new ValidationError({ message: `Template "${template}" contains no {field} tokens.` });
|
|
51
|
+
const fields = yield* sql.unsafe("SELECT * FROM fields WHERE model_id = ? ORDER BY position", [model.id]);
|
|
52
|
+
const fieldsByApiKey = new Map(fields.map((f) => [f.api_key, f]));
|
|
53
|
+
const neededColumns = new Set([
|
|
54
|
+
"id",
|
|
55
|
+
"_published_at",
|
|
56
|
+
"_updated_at"
|
|
57
|
+
]);
|
|
58
|
+
for (const token of tokens) neededColumns.add(token.segments[0]);
|
|
59
|
+
const columnList = [...neededColumns].map((c) => `"${c}"`).join(", ");
|
|
60
|
+
const records = yield* sql.unsafe(`SELECT ${columnList} FROM "content_${modelApiKey}" WHERE "_status" IN ('published', 'updated')`);
|
|
61
|
+
if (records.length === 0) return [];
|
|
62
|
+
const linkTokens = tokens.filter((t) => t.segments.length > 1);
|
|
63
|
+
const resolvedValues = /* @__PURE__ */ new Map();
|
|
64
|
+
for (const rec of records) resolvedValues.set(rec.id, /* @__PURE__ */ new Map());
|
|
65
|
+
for (const token of tokens) if (token.segments.length === 1) {
|
|
66
|
+
const fieldName = token.segments[0];
|
|
67
|
+
for (const rec of records) {
|
|
68
|
+
const value = rec[fieldName];
|
|
69
|
+
if (value !== null && value !== void 0) resolvedValues.get(rec.id).set(token.raw, String(value));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (linkTokens.length > 0) yield* resolveLinkedTokens(sql, linkTokens, records, fieldsByApiKey, resolvedValues);
|
|
73
|
+
const results = [];
|
|
74
|
+
for (const rec of records) {
|
|
75
|
+
const recId = rec.id;
|
|
76
|
+
const values = resolvedValues.get(recId);
|
|
77
|
+
const path = template.replace(/\{([^}]+)\}/g, (_match, tokenRaw) => {
|
|
78
|
+
const resolved = values.get(tokenRaw);
|
|
79
|
+
if (resolved !== void 0) return encodeURIComponent(resolved);
|
|
80
|
+
return `{${tokenRaw}}`;
|
|
81
|
+
});
|
|
82
|
+
const publishedAt = rec._published_at;
|
|
83
|
+
const updatedAt = rec._updated_at;
|
|
84
|
+
const lastmod = laterTimestamp(publishedAt, updatedAt) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
85
|
+
results.push({
|
|
86
|
+
id: recId,
|
|
87
|
+
path,
|
|
88
|
+
lastmod
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return results;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function laterTimestamp(a, b) {
|
|
95
|
+
if (!a && !b) return null;
|
|
96
|
+
if (!a) return b;
|
|
97
|
+
if (!b) return a;
|
|
98
|
+
return a > b ? a : b;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Resolve multi-segment tokens by traversing link fields breadth-first.
|
|
102
|
+
*
|
|
103
|
+
* For each hop level, we:
|
|
104
|
+
* 1. Collect all link IDs we need to fetch
|
|
105
|
+
* 2. Look up the target model from field validators
|
|
106
|
+
* 3. Batch-fetch all linked records
|
|
107
|
+
* 4. Continue to next hop with the linked records
|
|
108
|
+
*/
|
|
109
|
+
function resolveLinkedTokens(sql, tokens, records, fieldsByApiKey, resolvedValues) {
|
|
110
|
+
return Effect.gen(function* () {
|
|
111
|
+
const tokenFrontiers = /* @__PURE__ */ new Map();
|
|
112
|
+
for (const token of tokens) {
|
|
113
|
+
const frontierMap = /* @__PURE__ */ new Map();
|
|
114
|
+
for (const rec of records) {
|
|
115
|
+
const recId = rec.id;
|
|
116
|
+
const frontier = /* @__PURE__ */ new Map();
|
|
117
|
+
frontier.set(recId, rec);
|
|
118
|
+
frontierMap.set(recId, frontier);
|
|
119
|
+
}
|
|
120
|
+
tokenFrontiers.set(token.raw, frontierMap);
|
|
121
|
+
}
|
|
122
|
+
for (const token of tokens) {
|
|
123
|
+
const segments = token.segments;
|
|
124
|
+
let currentRecords = /* @__PURE__ */ new Map();
|
|
125
|
+
for (const rec of records) currentRecords.set(rec.id, rec);
|
|
126
|
+
let currentFieldsByApiKey = fieldsByApiKey;
|
|
127
|
+
let hopsCompleted = 0;
|
|
128
|
+
const hopsNeeded = segments.length - 1;
|
|
129
|
+
for (let hop = 0; hop < hopsNeeded; hop++) {
|
|
130
|
+
if (hop >= MAX_DEPTH) break;
|
|
131
|
+
const fieldName = segments[hop];
|
|
132
|
+
const field = currentFieldsByApiKey.get(fieldName);
|
|
133
|
+
if (!field || field.field_type !== "link") break;
|
|
134
|
+
const targetApiKeys = getLinkTargets(parseValidators(field.validators));
|
|
135
|
+
if (!targetApiKeys || targetApiKeys.length === 0) break;
|
|
136
|
+
const linkIdToOriginalRecords = /* @__PURE__ */ new Map();
|
|
137
|
+
for (const [origId, rec] of currentRecords) {
|
|
138
|
+
const linkId = rec[fieldName];
|
|
139
|
+
if (typeof linkId === "string" && linkId) {
|
|
140
|
+
const list = linkIdToOriginalRecords.get(linkId) ?? [];
|
|
141
|
+
list.push(origId);
|
|
142
|
+
linkIdToOriginalRecords.set(linkId, list);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const linkedRecords = /* @__PURE__ */ new Map();
|
|
146
|
+
if (linkIdToOriginalRecords.size > 0) {
|
|
147
|
+
const idsToFetch = [...linkIdToOriginalRecords.keys()];
|
|
148
|
+
for (const targetApiKey of targetApiKeys) {
|
|
149
|
+
if (idsToFetch.length === 0) break;
|
|
150
|
+
const placeholders = idsToFetch.map(() => "?").join(", ");
|
|
151
|
+
const rows = yield* sql.unsafe(`SELECT * FROM "content_${targetApiKey}" WHERE "id" IN (${placeholders})`, idsToFetch);
|
|
152
|
+
for (const row of rows) linkedRecords.set(row.id, row);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const nextRecords = /* @__PURE__ */ new Map();
|
|
156
|
+
for (const [origId, rec] of currentRecords) {
|
|
157
|
+
const linkId = rec[fieldName];
|
|
158
|
+
if (typeof linkId === "string" && linkId) {
|
|
159
|
+
const linked = linkedRecords.get(linkId);
|
|
160
|
+
if (linked) nextRecords.set(origId, linked);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
currentRecords = nextRecords;
|
|
164
|
+
const targetModel = yield* sql.unsafe(`SELECT * FROM "models" WHERE "api_key" = ?`, [targetApiKeys[0]]);
|
|
165
|
+
if (targetModel.length === 0) break;
|
|
166
|
+
const targetFields = yield* sql.unsafe(`SELECT * FROM "fields" WHERE "model_id" = ? ORDER BY position`, [targetModel[0].id]);
|
|
167
|
+
currentFieldsByApiKey = new Map(targetFields.map((f) => [f.api_key, f]));
|
|
168
|
+
hopsCompleted++;
|
|
169
|
+
}
|
|
170
|
+
if (hopsCompleted === hopsNeeded) {
|
|
171
|
+
const leafField = segments[segments.length - 1];
|
|
172
|
+
for (const [origId, linkedRec] of currentRecords) {
|
|
173
|
+
const value = linkedRec[leafField];
|
|
174
|
+
if (value !== null && value !== void 0) resolvedValues.get(origId).set(token.raw, String(value));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
//#endregion
|
|
7
181
|
//#region src/migrations.ts
|
|
8
182
|
/**
|
|
9
183
|
* Embedded schema migrations — runs automatically on first request.
|
|
@@ -15,6 +189,8 @@ const MIGRATIONS = [{
|
|
|
15
189
|
`CREATE TABLE IF NOT EXISTS "assets" (
|
|
16
190
|
"id" text PRIMARY KEY,
|
|
17
191
|
"filename" text NOT NULL,
|
|
192
|
+
"basename" text,
|
|
193
|
+
"format" text,
|
|
18
194
|
"mime_type" text NOT NULL,
|
|
19
195
|
"size" integer NOT NULL,
|
|
20
196
|
"width" integer,
|
|
@@ -43,6 +219,7 @@ const MIGRATIONS = [{
|
|
|
43
219
|
"has_draft" integer DEFAULT true NOT NULL,
|
|
44
220
|
"all_locales_required" integer DEFAULT 0 NOT NULL,
|
|
45
221
|
"ordering" text,
|
|
222
|
+
"canonical_path_template" text,
|
|
46
223
|
"created_at" text NOT NULL,
|
|
47
224
|
"updated_at" text NOT NULL
|
|
48
225
|
)`,
|
|
@@ -117,24 +294,14 @@ const MIGRATIONS = [{
|
|
|
117
294
|
"last_used_at" TEXT,
|
|
118
295
|
"expires_at" TEXT
|
|
119
296
|
)`,
|
|
120
|
-
`CREATE UNIQUE INDEX IF NOT EXISTS "idx_editor_tokens_secret_hash" ON "editor_tokens" ("secret_hash")
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
`
|
|
128
|
-
SET "basename" = CASE
|
|
129
|
-
WHEN instr("filename", '.') > 0 THEN substr("filename", 1, length("filename") - length(substr("filename", instr("filename", '.') + 1)) - 1)
|
|
130
|
-
ELSE "filename"
|
|
131
|
-
END`,
|
|
132
|
-
`UPDATE "assets"
|
|
133
|
-
SET "format" = lower(CASE
|
|
134
|
-
WHEN instr("filename", '.') > 0 THEN substr("filename", instr("filename", '.') + 1)
|
|
135
|
-
WHEN instr("mime_type", '/') > 0 THEN substr("mime_type", instr("mime_type", '/') + 1)
|
|
136
|
-
ELSE 'bin'
|
|
137
|
-
END)`,
|
|
297
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS "idx_editor_tokens_secret_hash" ON "editor_tokens" ("secret_hash")`,
|
|
298
|
+
`CREATE TABLE IF NOT EXISTS "preview_tokens" (
|
|
299
|
+
"id" text PRIMARY KEY,
|
|
300
|
+
"token_hash" text NOT NULL UNIQUE,
|
|
301
|
+
"expires_at" text NOT NULL,
|
|
302
|
+
"created_at" text NOT NULL DEFAULT (datetime('now'))
|
|
303
|
+
)`,
|
|
304
|
+
`CREATE INDEX IF NOT EXISTS "idx_preview_tokens_hash" ON "preview_tokens" ("token_hash")`,
|
|
138
305
|
`CREATE INDEX IF NOT EXISTS "idx_assets_basename" ON "assets" ("basename")`,
|
|
139
306
|
`CREATE INDEX IF NOT EXISTS "idx_assets_format" ON "assets" ("format")`
|
|
140
307
|
]
|
|
@@ -182,6 +349,159 @@ function actorFromHeaders(headers) {
|
|
|
182
349
|
};
|
|
183
350
|
}
|
|
184
351
|
//#endregion
|
|
352
|
+
//#region src/http/api/models.ts
|
|
353
|
+
/**
|
|
354
|
+
* HttpApiGroup for content model endpoints.
|
|
355
|
+
*
|
|
356
|
+
* Defines the declarative API shape — handlers are implemented separately
|
|
357
|
+
* via HttpApiBuilder.group().
|
|
358
|
+
*/
|
|
359
|
+
const ModelResponse = Schema.Struct({
|
|
360
|
+
id: Schema.String,
|
|
361
|
+
name: Schema.String,
|
|
362
|
+
apiKey: Schema.String,
|
|
363
|
+
isBlock: Schema.Boolean,
|
|
364
|
+
singleton: Schema.Boolean,
|
|
365
|
+
sortable: Schema.Boolean,
|
|
366
|
+
tree: Schema.Boolean,
|
|
367
|
+
hasDraft: Schema.Boolean,
|
|
368
|
+
allLocalesRequired: Schema.Boolean,
|
|
369
|
+
ordering: Schema.NullOr(Schema.String),
|
|
370
|
+
canonicalPathTemplate: Schema.NullOr(Schema.String),
|
|
371
|
+
createdAt: Schema.String,
|
|
372
|
+
updatedAt: Schema.String
|
|
373
|
+
});
|
|
374
|
+
const ModelWithFieldsResponse = Schema.Struct({
|
|
375
|
+
id: Schema.String,
|
|
376
|
+
name: Schema.String,
|
|
377
|
+
api_key: Schema.String,
|
|
378
|
+
is_block: Schema.Number,
|
|
379
|
+
singleton: Schema.Number,
|
|
380
|
+
sortable: Schema.Number,
|
|
381
|
+
tree: Schema.Number,
|
|
382
|
+
has_draft: Schema.Number,
|
|
383
|
+
all_locales_required: Schema.Number,
|
|
384
|
+
ordering: Schema.NullOr(Schema.String),
|
|
385
|
+
canonical_path_template: Schema.NullOr(Schema.String),
|
|
386
|
+
created_at: Schema.String,
|
|
387
|
+
updated_at: Schema.String,
|
|
388
|
+
fields: Schema.Array(Schema.Unknown)
|
|
389
|
+
});
|
|
390
|
+
const DeleteModelResponse = Schema.Struct({
|
|
391
|
+
deleted: Schema.Boolean,
|
|
392
|
+
recordsDestroyed: Schema.Number
|
|
393
|
+
});
|
|
394
|
+
const modelsGroup = HttpApiGroup.make("models").annotate(OpenApi.Title, "Models").annotate(OpenApi.Description, "Content model management").add(HttpApiEndpoint.get("listModels", "/models").annotate(OpenApi.Summary, "List all content models").addSuccess(Schema.Array(ModelResponse))).add(HttpApiEndpoint.post("createModel", "/models").annotate(OpenApi.Summary, "Create a new content model").setPayload(CreateModelInput).addSuccess(ModelResponse, { status: 201 })).add(HttpApiEndpoint.get("getModel", "/models/:id").annotate(OpenApi.Summary, "Get a content model by ID or api_key").setPath(Schema.Struct({ id: Schema.String })).addSuccess(ModelWithFieldsResponse)).add(HttpApiEndpoint.patch("updateModel", "/models/:id").annotate(OpenApi.Summary, "Update a content model").setPath(Schema.Struct({ id: Schema.String })).setPayload(UpdateModelInput).addSuccess(ModelResponse)).add(HttpApiEndpoint.del("deleteModel", "/models/:id").annotate(OpenApi.Summary, "Delete a content model").setPath(Schema.Struct({ id: Schema.String })).addSuccess(DeleteModelResponse));
|
|
395
|
+
//#endregion
|
|
396
|
+
//#region src/http/api/fields.ts
|
|
397
|
+
/**
|
|
398
|
+
* HttpApiGroup for field endpoints.
|
|
399
|
+
*
|
|
400
|
+
* Defines the declarative API shape — handlers are implemented separately
|
|
401
|
+
* via HttpApiBuilder.group().
|
|
402
|
+
*/
|
|
403
|
+
const fieldsGroup = HttpApiGroup.make("fields").annotate(OpenApi.Title, "Fields").annotate(OpenApi.Description, "Content model field management").add(HttpApiEndpoint.get("listFields", "/models/:modelId/fields").annotate(OpenApi.Summary, "List all fields for a model").setPath(Schema.Struct({ modelId: Schema.String })).addSuccess(Schema.Array(Schema.Unknown))).add(HttpApiEndpoint.post("createField", "/models/:modelId/fields").annotate(OpenApi.Summary, "Create a new field on a model").setPath(Schema.Struct({ modelId: Schema.String })).setPayload(CreateFieldInput).addSuccess(Schema.Unknown, { status: 201 })).add(HttpApiEndpoint.patch("updateField", "/models/:modelId/fields/:fieldId").annotate(OpenApi.Summary, "Update a field").setPath(Schema.Struct({
|
|
404
|
+
modelId: Schema.String,
|
|
405
|
+
fieldId: Schema.String
|
|
406
|
+
})).setPayload(UpdateFieldInput).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.del("deleteField", "/models/:modelId/fields/:fieldId").annotate(OpenApi.Summary, "Delete a field").setPath(Schema.Struct({
|
|
407
|
+
modelId: Schema.String,
|
|
408
|
+
fieldId: Schema.String
|
|
409
|
+
})).addSuccess(Schema.Unknown));
|
|
410
|
+
//#endregion
|
|
411
|
+
//#region src/http/api/records.ts
|
|
412
|
+
/**
|
|
413
|
+
* HttpApiGroup for record endpoints.
|
|
414
|
+
*
|
|
415
|
+
* Defines the declarative API shape — handlers are implemented separately
|
|
416
|
+
* via HttpApiBuilder.group().
|
|
417
|
+
*/
|
|
418
|
+
const ModelApiKeyParams = Schema.Struct({ modelApiKey: Schema.String });
|
|
419
|
+
const IdPath = Schema.Struct({ id: Schema.String });
|
|
420
|
+
const IdVersionPath = Schema.Struct({
|
|
421
|
+
id: Schema.String,
|
|
422
|
+
versionId: Schema.String
|
|
423
|
+
});
|
|
424
|
+
const recordsGroup = HttpApiGroup.make("records").annotate(OpenApi.Title, "Records").annotate(OpenApi.Description, "Content record management").add(HttpApiEndpoint.post("bulkCreateRecords", "/records/bulk").annotate(OpenApi.Summary, "Bulk create records").setPayload(BulkCreateRecordsInput).addSuccess(Schema.Unknown, { status: 201 })).add(HttpApiEndpoint.post("createRecord", "/records").annotate(OpenApi.Summary, "Create a record").setPayload(CreateRecordInput).addSuccess(Schema.Unknown, { status: 201 })).add(HttpApiEndpoint.get("listRecords", "/records").annotate(OpenApi.Summary, "List records for a model").setUrlParams(ModelApiKeyParams).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.get("listVersions", "/records/:id/versions").annotate(OpenApi.Summary, "List versions for a record").setPath(IdPath).setUrlParams(ModelApiKeyParams).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.get("getVersion", "/records/:id/versions/:versionId").annotate(OpenApi.Summary, "Get a specific version").setPath(IdVersionPath).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.post("restoreVersion", "/records/:id/versions/:versionId/restore").annotate(OpenApi.Summary, "Restore a record to a previous version").setPath(IdVersionPath).setUrlParams(ModelApiKeyParams).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.get("getRecord", "/records/:id").annotate(OpenApi.Summary, "Get a record by ID").setPath(IdPath).setUrlParams(ModelApiKeyParams).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.patch("updateRecord", "/records/:id").annotate(OpenApi.Summary, "Update a record").setPath(IdPath).setPayload(PatchRecordInput).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.patch("patchBlocks", "/records/:id/blocks").annotate(OpenApi.Summary, "Patch structured text blocks on a record").setPath(IdPath).setPayload(PatchBlocksInput).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.del("deleteRecord", "/records/:id").annotate(OpenApi.Summary, "Delete a record").setPath(IdPath).setUrlParams(ModelApiKeyParams).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.post("publishRecord", "/records/:id/publish").annotate(OpenApi.Summary, "Publish a record").setPath(IdPath).setUrlParams(ModelApiKeyParams).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.post("unpublishRecord", "/records/:id/unpublish").annotate(OpenApi.Summary, "Unpublish a record").setPath(IdPath).setUrlParams(ModelApiKeyParams).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.post("schedulePublish", "/records/:id/schedule-publish").annotate(OpenApi.Summary, "Schedule a record for publishing").setPath(IdPath).setPayload(ScheduleRecordInput).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.post("scheduleUnpublish", "/records/:id/schedule-unpublish").annotate(OpenApi.Summary, "Schedule a record for unpublishing").setPath(IdPath).setPayload(ScheduleRecordInput).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.post("clearSchedule", "/records/:id/clear-schedule").annotate(OpenApi.Summary, "Clear scheduled publish/unpublish").setPath(IdPath).setPayload(Schema.Struct({ modelApiKey: Schema.NonEmptyString })).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.post("reorderRecords", "/reorder").annotate(OpenApi.Summary, "Reorder records within a model").setPayload(ReorderInput).addSuccess(Schema.Unknown));
|
|
425
|
+
//#endregion
|
|
426
|
+
//#region src/http/api/assets.ts
|
|
427
|
+
/**
|
|
428
|
+
* HttpApiGroup for asset endpoints.
|
|
429
|
+
*
|
|
430
|
+
* Defines the declarative API shape — handlers are implemented separately
|
|
431
|
+
* via HttpApiBuilder.group().
|
|
432
|
+
*/
|
|
433
|
+
const assetsGroup = HttpApiGroup.make("assets").annotate(OpenApi.Title, "Assets").annotate(OpenApi.Description, "Asset management").add(HttpApiEndpoint.get("listAssets", "/assets").annotate(OpenApi.Summary, "List or search assets").setUrlParams(Schema.Struct({
|
|
434
|
+
q: Schema.optional(Schema.String),
|
|
435
|
+
limit: Schema.optional(Schema.String),
|
|
436
|
+
offset: Schema.optional(Schema.String)
|
|
437
|
+
})).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.post("createAsset", "/assets").annotate(OpenApi.Summary, "Create a new asset").setPayload(CreateAssetInput).addSuccess(Schema.Unknown, { status: 201 })).add(HttpApiEndpoint.get("getAsset", "/assets/:id").annotate(OpenApi.Summary, "Get an asset by ID").setPath(Schema.Struct({ id: Schema.String })).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.put("replaceAsset", "/assets/:id").annotate(OpenApi.Summary, "Replace an asset").setPath(Schema.Struct({ id: Schema.String })).setPayload(CreateAssetInput).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.patch("updateAssetMetadata", "/assets/:id").annotate(OpenApi.Summary, "Update asset metadata").setPath(Schema.Struct({ id: Schema.String })).setPayload(UpdateAssetMetadataInput).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.del("deleteAsset", "/assets/:id").annotate(OpenApi.Summary, "Delete an asset").setPath(Schema.Struct({ id: Schema.String })).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.post("createUploadUrl", "/assets/upload-url").annotate(OpenApi.Summary, "Create a presigned upload URL").setPayload(CreateUploadUrlInput).addSuccess(Schema.Unknown));
|
|
438
|
+
//#endregion
|
|
439
|
+
//#region src/http/api/locales.ts
|
|
440
|
+
/**
|
|
441
|
+
* HttpApiGroup for locale endpoints.
|
|
442
|
+
*
|
|
443
|
+
* Defines the declarative API shape — handlers are implemented separately
|
|
444
|
+
* via HttpApiBuilder.group().
|
|
445
|
+
*/
|
|
446
|
+
const localesGroup = HttpApiGroup.make("locales").annotate(OpenApi.Title, "Locales").annotate(OpenApi.Description, "Locale management").add(HttpApiEndpoint.get("listLocales", "/locales").annotate(OpenApi.Summary, "List all locales").addSuccess(Schema.Unknown)).add(HttpApiEndpoint.post("createLocale", "/locales").annotate(OpenApi.Summary, "Create a new locale").setPayload(CreateLocaleInput).addSuccess(Schema.Unknown, { status: 201 })).add(HttpApiEndpoint.del("deleteLocale", "/locales/:id").annotate(OpenApi.Summary, "Delete a locale").setPath(Schema.Struct({ id: Schema.String })).addSuccess(Schema.Unknown));
|
|
447
|
+
//#endregion
|
|
448
|
+
//#region src/http/api/schema-io.ts
|
|
449
|
+
/**
|
|
450
|
+
* HttpApiGroup for schema import/export endpoints.
|
|
451
|
+
*
|
|
452
|
+
* Defines the declarative API shape — handlers are implemented separately
|
|
453
|
+
* via HttpApiBuilder.group().
|
|
454
|
+
*/
|
|
455
|
+
const schemaGroup = HttpApiGroup.make("schema").annotate(OpenApi.Title, "Schema").annotate(OpenApi.Description, "Schema import and export").add(HttpApiEndpoint.get("exportSchema", "/schema").annotate(OpenApi.Summary, "Export the full schema").addSuccess(Schema.Unknown)).add(HttpApiEndpoint.post("importSchema", "/schema").annotate(OpenApi.Summary, "Import a schema").setPayload(ImportSchemaInput).addSuccess(Schema.Unknown, { status: 201 }));
|
|
456
|
+
//#endregion
|
|
457
|
+
//#region src/http/api/search.ts
|
|
458
|
+
/**
|
|
459
|
+
* HttpApiGroup for search endpoints.
|
|
460
|
+
*
|
|
461
|
+
* Defines the declarative API shape — handlers are implemented separately
|
|
462
|
+
* via HttpApiBuilder.group().
|
|
463
|
+
*/
|
|
464
|
+
const searchGroup = HttpApiGroup.make("search").annotate(OpenApi.Title, "Search").annotate(OpenApi.Description, "Full-text and vector search").add(HttpApiEndpoint.post("search", "/search").annotate(OpenApi.Summary, "Search content").setPayload(SearchInput).addSuccess(Schema.Unknown)).add(HttpApiEndpoint.post("reindexSearch", "/search/reindex").annotate(OpenApi.Summary, "Reindex search").setPayload(ReindexSearchInput).addSuccess(Schema.Unknown));
|
|
465
|
+
//#endregion
|
|
466
|
+
//#region src/http/api/tokens.ts
|
|
467
|
+
/**
|
|
468
|
+
* HttpApiGroup for editor token endpoints.
|
|
469
|
+
*
|
|
470
|
+
* Defines the declarative API shape — handlers are implemented separately
|
|
471
|
+
* via HttpApiBuilder.group().
|
|
472
|
+
*/
|
|
473
|
+
const tokensGroup = HttpApiGroup.make("tokens").annotate(OpenApi.Title, "Tokens").annotate(OpenApi.Description, "Editor token management").add(HttpApiEndpoint.get("listEditorTokens", "/tokens").annotate(OpenApi.Summary, "List all editor tokens").addSuccess(Schema.Unknown)).add(HttpApiEndpoint.post("createEditorToken", "/tokens").annotate(OpenApi.Summary, "Create an editor token").setPayload(CreateEditorTokenInput).addSuccess(Schema.Unknown, { status: 201 })).add(HttpApiEndpoint.del("revokeEditorToken", "/tokens/:id").annotate(OpenApi.Summary, "Revoke an editor token").setPath(Schema.Struct({ id: Schema.String })).addSuccess(Schema.Unknown));
|
|
474
|
+
//#endregion
|
|
475
|
+
//#region src/http/api/preview-tokens.ts
|
|
476
|
+
/**
|
|
477
|
+
* HttpApiGroup for preview token endpoints.
|
|
478
|
+
*
|
|
479
|
+
* Defines the declarative API shape — handlers are implemented separately
|
|
480
|
+
* via HttpApiBuilder.group().
|
|
481
|
+
*/
|
|
482
|
+
const previewTokensGroup = HttpApiGroup.make("preview-tokens").annotate(OpenApi.Title, "Preview Tokens").annotate(OpenApi.Description, "Preview token creation and validation").add(HttpApiEndpoint.post("createPreviewToken", "/preview-tokens").annotate(OpenApi.Summary, "Create a preview token").setPayload(Schema.Struct({ expiresIn: Schema.optional(Schema.Number) })).addSuccess(Schema.Unknown, { status: 201 })).add(HttpApiEndpoint.get("validatePreviewToken", "/preview-tokens/validate").annotate(OpenApi.Summary, "Validate a preview token").setUrlParams(Schema.Struct({ token: Schema.String })).addSuccess(Schema.Unknown));
|
|
483
|
+
//#endregion
|
|
484
|
+
//#region src/http/api/paths.ts
|
|
485
|
+
/**
|
|
486
|
+
* HttpApiGroup for canonical path resolution endpoints.
|
|
487
|
+
*
|
|
488
|
+
* Defines the declarative API shape — handlers are implemented separately
|
|
489
|
+
* via HttpApiBuilder.group().
|
|
490
|
+
*/
|
|
491
|
+
const pathsGroup = HttpApiGroup.make("paths").annotate(OpenApi.Title, "Paths").annotate(OpenApi.Description, "Canonical path resolution").add(HttpApiEndpoint.get("resolveCanonicalPaths", "/paths/:modelApiKey").annotate(OpenApi.Summary, "Resolve canonical paths for a model").setPath(Schema.Struct({ modelApiKey: Schema.String })).addSuccess(Schema.Unknown));
|
|
492
|
+
//#endregion
|
|
493
|
+
//#region src/http/api/index.ts
|
|
494
|
+
/**
|
|
495
|
+
* Declarative HttpApi definition for the agent-cms REST API.
|
|
496
|
+
*
|
|
497
|
+
* Composes all endpoint groups and generates the OpenAPI 3.1.0 spec
|
|
498
|
+
* via `OpenApi.fromApi()`. The spec is served at /openapi.json by
|
|
499
|
+
* the router.
|
|
500
|
+
*/
|
|
501
|
+
const cmsApi = HttpApi.make("agent-cms").annotate(OpenApi.Title, "Agent CMS API").annotate(OpenApi.Version, "1.0.0").annotate(OpenApi.Description, "Headless CMS REST API for content management").add(modelsGroup).add(fieldsGroup).add(recordsGroup).add(assetsGroup).add(localesGroup).add(schemaGroup).add(searchGroup).add(tokensGroup).add(previewTokensGroup).add(pathsGroup);
|
|
502
|
+
/** Pre-generated OpenAPI 3.1.0 specification */
|
|
503
|
+
const openApiSpec = OpenApi.fromApi(cmsApi);
|
|
504
|
+
//#endregion
|
|
185
505
|
//#region src/http/router.ts
|
|
186
506
|
function describeUnknown(error) {
|
|
187
507
|
if (error instanceof Error) return `${error.name}: ${error.message}`;
|
|
@@ -378,9 +698,19 @@ const tokensRouter = HttpRouter.empty.pipe(HttpRouter.get("/", handle(listEditor
|
|
|
378
698
|
})), HttpRouter.del("/:id", Effect.gen(function* () {
|
|
379
699
|
return yield* handle(revokeEditorToken(param(yield* HttpRouter.params, "id")));
|
|
380
700
|
})));
|
|
701
|
+
const previewTokensRouter = HttpRouter.empty.pipe(HttpRouter.post("/", Effect.gen(function* () {
|
|
702
|
+
const body = yield* readJsonBody();
|
|
703
|
+
return yield* handle(createPreviewToken(typeof body === "object" && body !== null && "expiresIn" in body ? body.expiresIn : void 0), 201);
|
|
704
|
+
})), HttpRouter.get("/validate", Effect.gen(function* () {
|
|
705
|
+
return yield* handle(validatePreviewToken(yield* queryParam("token")));
|
|
706
|
+
})));
|
|
707
|
+
const pathsRouter = HttpRouter.empty.pipe(HttpRouter.get("/:modelApiKey", Effect.gen(function* () {
|
|
708
|
+
return yield* handle(resolveCanonicalPaths(param(yield* HttpRouter.params, "modelApiKey")));
|
|
709
|
+
})));
|
|
381
710
|
const setupRouter = HttpRouter.empty.pipe(HttpRouter.post("/setup", handle(ensureSchema().pipe(Effect.as({ ok: true })))));
|
|
382
711
|
const healthRouter = HttpRouter.empty.pipe(HttpRouter.get("/health", HttpServerResponse.json({ status: "ok" })));
|
|
383
|
-
const
|
|
712
|
+
const openApiRouter = HttpRouter.empty.pipe(HttpRouter.get("/openapi.json", HttpServerResponse.json(openApiSpec)));
|
|
713
|
+
const appRouter = HttpRouter.empty.pipe(HttpRouter.concat(openApiRouter), HttpRouter.concat(healthRouter), HttpRouter.concat(modelsRouter.pipe(HttpRouter.prefixAll("/api/models"))), HttpRouter.concat(fieldsRouter.pipe(HttpRouter.prefixAll("/api"))), HttpRouter.concat(recordsRouter.pipe(HttpRouter.prefixAll("/api"))), HttpRouter.concat(assetsRouter.pipe(HttpRouter.prefixAll("/api/assets"))), HttpRouter.concat(localesRouter.pipe(HttpRouter.prefixAll("/api/locales"))), HttpRouter.concat(schemaRouter.pipe(HttpRouter.prefixAll("/api/schema"))), HttpRouter.concat(searchRouter.pipe(HttpRouter.prefixAll("/api/search"))), HttpRouter.concat(tokensRouter.pipe(HttpRouter.prefixAll("/api/tokens"))), HttpRouter.concat(previewTokensRouter.pipe(HttpRouter.prefixAll("/api/preview-tokens"))), HttpRouter.concat(setupRouter.pipe(HttpRouter.prefixAll("/api"))), HttpRouter.concat(pathsRouter.pipe(HttpRouter.prefixAll("/paths"))));
|
|
384
714
|
function createWebHandler(sqlLayer, options) {
|
|
385
715
|
const vectorizeLayer = Layer.succeed(VectorizeContext, options?.ai && options.vectorize ? Option.some({
|
|
386
716
|
ai: options.ai,
|
|
@@ -415,7 +745,7 @@ function createWebHandler(sqlLayer, options) {
|
|
|
415
745
|
}
|
|
416
746
|
async function getGraphqlInstance() {
|
|
417
747
|
if (!graphqlInstance) {
|
|
418
|
-
if (!graphqlModulePromise) graphqlModulePromise = import("./handler-
|
|
748
|
+
if (!graphqlModulePromise) graphqlModulePromise = import("./handler-B5jgfPOY.mjs");
|
|
419
749
|
graphqlInstance = (await graphqlModulePromise).createGraphQLHandler(sqlLayer, {
|
|
420
750
|
assetBaseUrl: options?.assetBaseUrl,
|
|
421
751
|
isProduction: options?.isProduction
|
|
@@ -442,7 +772,7 @@ function createWebHandler(sqlLayer, options) {
|
|
|
442
772
|
const headers = new Headers(response.headers);
|
|
443
773
|
headers.set("Access-Control-Allow-Origin", origin);
|
|
444
774
|
headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
|
|
445
|
-
headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Include-Drafts, X-Exclude-Invalid, X-Filename, X-Requested-With, Accept, User-Agent");
|
|
775
|
+
headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Include-Drafts, X-Exclude-Invalid, X-Filename, X-Requested-With, Accept, User-Agent, X-Preview-Token");
|
|
446
776
|
headers.set("Access-Control-Max-Age", "600");
|
|
447
777
|
return new Response(response.body, {
|
|
448
778
|
status: response.status,
|
|
@@ -559,6 +889,10 @@ function createWebHandler(sqlLayer, options) {
|
|
|
559
889
|
const h = new Headers(instrumentedRequest.headers);
|
|
560
890
|
if (credentialType) h.set("X-Credential-Type", credentialType);
|
|
561
891
|
else h.delete("X-Credential-Type");
|
|
892
|
+
const previewToken = instrumentedRequest.headers.get("X-Preview-Token");
|
|
893
|
+
if (previewToken) try {
|
|
894
|
+
if ((await Effect.runPromise(validatePreviewToken(previewToken).pipe(Effect.provide(fullLayer)))).valid) h.set("X-Include-Drafts", "true");
|
|
895
|
+
} catch {}
|
|
562
896
|
instrumentedRequest = new Request(instrumentedRequest, { headers: h });
|
|
563
897
|
} else if (url.pathname === "/mcp") {
|
|
564
898
|
if ((await getRequestActor(instrumentedRequest))?.type === "editor") return finish(new Response(JSON.stringify({ error: "Unauthorized. Editor tokens must use /mcp/editor." }), {
|
|
@@ -582,7 +916,7 @@ function createWebHandler(sqlLayer, options) {
|
|
|
582
916
|
headers: { "Content-Type": "application/json" }
|
|
583
917
|
}));
|
|
584
918
|
}
|
|
585
|
-
} else if (url.pathname.startsWith("/api/")) {
|
|
919
|
+
} else if (url.pathname === "/api/preview-tokens/validate") {} else if (url.pathname.startsWith("/api/")) {
|
|
586
920
|
const adminOnly = isSchemaMutationRequest(url, instrumentedRequest.method) || url.pathname.startsWith("/api/tokens");
|
|
587
921
|
const denied = await checkWriteAuth(instrumentedRequest, adminOnly);
|
|
588
922
|
if (denied) {
|
|
@@ -598,36 +932,88 @@ function createWebHandler(sqlLayer, options) {
|
|
|
598
932
|
instrumentedRequest = new Request(instrumentedRequest, { headers: h });
|
|
599
933
|
}
|
|
600
934
|
if (url.pathname === "/mcp") {
|
|
601
|
-
|
|
935
|
+
if (options?.loader) {
|
|
936
|
+
const { createMcpHttpHandler } = await import("./http-transport-DVKhbbe1.mjs");
|
|
937
|
+
const adminMcpHandler = mcpHandler ??= createMcpHttpHandler(fullLayer, {
|
|
938
|
+
mode: "admin",
|
|
939
|
+
path: "/mcp",
|
|
940
|
+
r2Bucket: options.r2Bucket,
|
|
941
|
+
assetBaseUrl: options.assetBaseUrl,
|
|
942
|
+
siteUrl: options.siteUrl,
|
|
943
|
+
actor: {
|
|
944
|
+
type: "admin",
|
|
945
|
+
label: "admin"
|
|
946
|
+
}
|
|
947
|
+
});
|
|
948
|
+
const { createCodeModeMcpServer } = await import("./codemode-handler-Bgu2utjN.mjs");
|
|
949
|
+
const { createMcpHandler } = await import("agents/mcp");
|
|
950
|
+
return finish(await createMcpHandler(await createCodeModeMcpServer({
|
|
951
|
+
loader: options.loader,
|
|
952
|
+
mcpHandler: adminMcpHandler
|
|
953
|
+
}), { route: "/mcp" })(instrumentedRequest, {}, {
|
|
954
|
+
waitUntil: () => {},
|
|
955
|
+
passThroughOnException: () => {}
|
|
956
|
+
}));
|
|
957
|
+
}
|
|
958
|
+
const { createMcpHttpHandler } = await import("./http-transport-DVKhbbe1.mjs");
|
|
602
959
|
const actor = await getRequestActor(instrumentedRequest);
|
|
603
960
|
return finish(await (actor?.type === "admin" ? mcpHandler ??= createMcpHttpHandler(fullLayer, {
|
|
604
961
|
mode: "admin",
|
|
605
962
|
path: "/mcp",
|
|
606
963
|
r2Bucket: options?.r2Bucket,
|
|
607
964
|
assetBaseUrl: options?.assetBaseUrl,
|
|
965
|
+
siteUrl: options?.siteUrl,
|
|
608
966
|
actor
|
|
609
967
|
}) : createMcpHttpHandler(fullLayer, {
|
|
610
968
|
mode: "admin",
|
|
611
969
|
path: "/mcp",
|
|
612
970
|
r2Bucket: options?.r2Bucket,
|
|
613
971
|
assetBaseUrl: options?.assetBaseUrl,
|
|
972
|
+
siteUrl: options?.siteUrl,
|
|
614
973
|
actor
|
|
615
974
|
}))(instrumentedRequest));
|
|
616
975
|
}
|
|
617
976
|
if (url.pathname === "/mcp/editor") {
|
|
618
|
-
|
|
977
|
+
if (options?.loader) {
|
|
978
|
+
const { createMcpHttpHandler } = await import("./http-transport-DVKhbbe1.mjs");
|
|
979
|
+
const editorMcpHandler = mcpEditorHandler ??= createMcpHttpHandler(fullLayer, {
|
|
980
|
+
mode: "editor",
|
|
981
|
+
path: "/mcp/editor",
|
|
982
|
+
r2Bucket: options.r2Bucket,
|
|
983
|
+
assetBaseUrl: options.assetBaseUrl,
|
|
984
|
+
siteUrl: options.siteUrl,
|
|
985
|
+
actor: {
|
|
986
|
+
type: "editor",
|
|
987
|
+
label: "editor"
|
|
988
|
+
}
|
|
989
|
+
});
|
|
990
|
+
const { createCodeModeMcpServer } = await import("./codemode-handler-Bgu2utjN.mjs");
|
|
991
|
+
const { createMcpHandler } = await import("agents/mcp");
|
|
992
|
+
return finish(await createMcpHandler(await createCodeModeMcpServer({
|
|
993
|
+
loader: options.loader,
|
|
994
|
+
mcpHandler: editorMcpHandler,
|
|
995
|
+
mode: "editor",
|
|
996
|
+
mcpPath: "/mcp/editor"
|
|
997
|
+
}), { route: "/mcp/editor" })(instrumentedRequest, {}, {
|
|
998
|
+
waitUntil: () => {},
|
|
999
|
+
passThroughOnException: () => {}
|
|
1000
|
+
}));
|
|
1001
|
+
}
|
|
1002
|
+
const { createMcpHttpHandler } = await import("./http-transport-DVKhbbe1.mjs");
|
|
619
1003
|
const actor = await getRequestActor(instrumentedRequest);
|
|
620
1004
|
return finish(await (actor?.type === "editor" ? createMcpHttpHandler(fullLayer, {
|
|
621
1005
|
mode: "editor",
|
|
622
1006
|
path: "/mcp/editor",
|
|
623
1007
|
r2Bucket: options?.r2Bucket,
|
|
624
1008
|
assetBaseUrl: options?.assetBaseUrl,
|
|
1009
|
+
siteUrl: options?.siteUrl,
|
|
625
1010
|
actor
|
|
626
1011
|
}) : mcpEditorHandler ??= createMcpHttpHandler(fullLayer, {
|
|
627
1012
|
mode: "editor",
|
|
628
1013
|
path: "/mcp/editor",
|
|
629
1014
|
r2Bucket: options?.r2Bucket,
|
|
630
1015
|
assetBaseUrl: options?.assetBaseUrl,
|
|
1016
|
+
siteUrl: options?.siteUrl,
|
|
631
1017
|
actor
|
|
632
1018
|
}))(instrumentedRequest));
|
|
633
1019
|
}
|
|
@@ -642,7 +1028,7 @@ function createWebHandler(sqlLayer, options) {
|
|
|
642
1028
|
if (!graphqlModulePromise) {
|
|
643
1029
|
graphqlImportCache = "miss";
|
|
644
1030
|
const importStartedAt = performance.now();
|
|
645
|
-
graphqlModulePromise = import("./handler-
|
|
1031
|
+
graphqlModulePromise = import("./handler-B5jgfPOY.mjs").then((module) => {
|
|
646
1032
|
graphqlImportMs = Number((performance.now() - importStartedAt).toFixed(3));
|
|
647
1033
|
return module;
|
|
648
1034
|
});
|
|
@@ -740,7 +1126,10 @@ function createWebHandler(sqlLayer, options) {
|
|
|
740
1126
|
async execute(query, variables, context) {
|
|
741
1127
|
return (await getGraphqlInstance()).execute(query, variables, context);
|
|
742
1128
|
},
|
|
743
|
-
runScheduledTransitions: runScheduledTransitions$1
|
|
1129
|
+
runScheduledTransitions: runScheduledTransitions$1,
|
|
1130
|
+
resolveCanonicalPaths(modelApiKey) {
|
|
1131
|
+
return Effect.runPromise(resolveCanonicalPaths(modelApiKey).pipe(Effect.provide(fullLayer)));
|
|
1132
|
+
}
|
|
744
1133
|
};
|
|
745
1134
|
}
|
|
746
1135
|
//#endregion
|
|
@@ -766,7 +1155,9 @@ const RawCmsBindingsSchema = Schema.Struct({
|
|
|
766
1155
|
r2AccessKeyId: OptionalNonEmptyString,
|
|
767
1156
|
r2SecretAccessKey: OptionalNonEmptyString,
|
|
768
1157
|
r2BucketName: OptionalNonEmptyString,
|
|
769
|
-
cfAccountId: OptionalNonEmptyString
|
|
1158
|
+
cfAccountId: OptionalNonEmptyString,
|
|
1159
|
+
siteUrl: Schema.optional(Schema.String),
|
|
1160
|
+
loader: Schema.optional(RuntimeObject)
|
|
770
1161
|
}).pipe(Schema.filter((bindings) => {
|
|
771
1162
|
return bindings.ai !== void 0 === (bindings.vectorize !== void 0);
|
|
772
1163
|
}, { message: () => "ai and vectorize bindings must be configured together" }), Schema.filter((bindings) => {
|
|
@@ -799,7 +1190,9 @@ function decodeCmsBindings(input) {
|
|
|
799
1190
|
secretAccessKey: bindings.r2SecretAccessKey,
|
|
800
1191
|
bucketName: bindings.r2BucketName,
|
|
801
1192
|
accountId: bindings.cfAccountId
|
|
802
|
-
} : void 0
|
|
1193
|
+
} : void 0,
|
|
1194
|
+
siteUrl: bindings.siteUrl,
|
|
1195
|
+
loader: bindings.loader
|
|
803
1196
|
};
|
|
804
1197
|
}
|
|
805
1198
|
//#endregion
|
|
@@ -1103,7 +1496,8 @@ function cacheKey(bindings, hooks) {
|
|
|
1103
1496
|
bindings.writeKey ?? "",
|
|
1104
1497
|
bindings.r2Credentials?.accessKeyId ?? "",
|
|
1105
1498
|
bindings.r2Credentials?.bucketName ?? "",
|
|
1106
|
-
bindings.r2Credentials?.accountId ?? ""
|
|
1499
|
+
bindings.r2Credentials?.accountId ?? "",
|
|
1500
|
+
bindings.siteUrl ?? ""
|
|
1107
1501
|
].join("|");
|
|
1108
1502
|
}
|
|
1109
1503
|
/**
|
|
@@ -1156,12 +1550,15 @@ function createCMSHandlerUncached(bindings, hooks) {
|
|
|
1156
1550
|
ai: bindings.ai,
|
|
1157
1551
|
vectorize: bindings.vectorize,
|
|
1158
1552
|
hooks,
|
|
1159
|
-
r2Credentials: bindings.r2Credentials
|
|
1553
|
+
r2Credentials: bindings.r2Credentials,
|
|
1554
|
+
siteUrl: bindings.siteUrl,
|
|
1555
|
+
loader: bindings.loader
|
|
1160
1556
|
});
|
|
1161
1557
|
return {
|
|
1162
1558
|
fetch: (request) => webHandler.fetch(request),
|
|
1163
1559
|
execute: webHandler.execute,
|
|
1164
|
-
runScheduledTransitions: (now) => webHandler.runScheduledTransitions(now)
|
|
1560
|
+
runScheduledTransitions: (now) => webHandler.runScheduledTransitions(now),
|
|
1561
|
+
resolveCanonicalPaths: (modelApiKey) => webHandler.resolveCanonicalPaths(modelApiKey)
|
|
1165
1562
|
};
|
|
1166
1563
|
}
|
|
1167
1564
|
//#endregion
|