@hollware/server 1.0.0-rc.0 → 1.0.0-rc.1
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/index.mjs +47 -10
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import { CLIENT_CAPABILITIES_META_KEY, CLIENT_INFO_META_KEY, McpServer, accepted
|
|
|
10
10
|
import * as z from "zod";
|
|
11
11
|
import { WorkerEntrypoint } from "cloudflare:workers";
|
|
12
12
|
//#region package.json
|
|
13
|
-
var version = "1.0.0-rc.
|
|
13
|
+
var version = "1.0.0-rc.1";
|
|
14
14
|
//#endregion
|
|
15
15
|
//#region src/auth/responses.ts
|
|
16
16
|
/** The challenge of every 401 and 403 (RFC 6750). */
|
|
@@ -2458,8 +2458,24 @@ function gatewayError(status, body) {
|
|
|
2458
2458
|
* in its fix, which is how the operator finds the line of the log that has the cause. The cause
|
|
2459
2459
|
* goes to the log and never to the caller, whose agent could repeat it to a person or to another
|
|
2460
2460
|
* system. This holds for every error, a failed D1 query included: nothing turns one into a 401.
|
|
2461
|
+
* - But a database that lacks the migrations of the release is a 503 `not_migrated` that says what
|
|
2462
|
+
* to run: an agent that set the project up can fix it, and the cause still goes to the log only.
|
|
2461
2463
|
* - A path that no route has is a JSON 404.
|
|
2462
2464
|
*/
|
|
2465
|
+
/** What D1 says when a table or a column of this release is not there: a migration is missing. */
|
|
2466
|
+
const MISSING_SCHEMA = /no such (?:table|column)|has no column named/;
|
|
2467
|
+
/**
|
|
2468
|
+
* Whether an error, or one of its causes, is D1 telling that the database lacks the schema of this
|
|
2469
|
+
* release: the one failure whose fix the caller can be told without anything of its cause.
|
|
2470
|
+
*/
|
|
2471
|
+
function isMissingMigration(error) {
|
|
2472
|
+
let current = error;
|
|
2473
|
+
for (let depth = 0; depth < 5 && current instanceof Error; depth++) {
|
|
2474
|
+
if (MISSING_SCHEMA.test(current.message)) return true;
|
|
2475
|
+
current = current.cause;
|
|
2476
|
+
}
|
|
2477
|
+
return false;
|
|
2478
|
+
}
|
|
2463
2479
|
function installErrorHandling(app, log) {
|
|
2464
2480
|
app.onError((c) => {
|
|
2465
2481
|
const requestId = c.get("requestId");
|
|
@@ -2470,6 +2486,11 @@ function installErrorHandling(app, log) {
|
|
|
2470
2486
|
path: c.req.path,
|
|
2471
2487
|
detail: describeError(c.error)
|
|
2472
2488
|
});
|
|
2489
|
+
if (isMissingMigration(c.error)) return gatewayError(503, {
|
|
2490
|
+
code: "not_migrated",
|
|
2491
|
+
message: "The database of this Hollware lacks the migrations of its release.",
|
|
2492
|
+
fix: "Apply them: \"hollware migrate --local\" in local development, \"hollware migrate --remote\" (or \"hollware deploy\") for a deployment. Then try again."
|
|
2493
|
+
});
|
|
2473
2494
|
return gatewayError(500, {
|
|
2474
2495
|
code: "internal",
|
|
2475
2496
|
message: "The request failed on the server.",
|
|
@@ -3877,6 +3898,8 @@ function installOpsApi(app, options) {
|
|
|
3877
3898
|
}
|
|
3878
3899
|
//#endregion
|
|
3879
3900
|
//#region src/http/read-api.ts
|
|
3901
|
+
/** The paths of the index of the read API, which answers with no token. */
|
|
3902
|
+
const INDEX_PATHS = /* @__PURE__ */ new Set(["/v1", "/v1/"]);
|
|
3880
3903
|
/** How long a browser keeps the answer of a preflight, in seconds. */
|
|
3881
3904
|
const PREFLIGHT_MAX_AGE = 600;
|
|
3882
3905
|
/** The origin of a request from a page of another origin than Hollware's own, or `null`. */
|
|
@@ -3976,7 +3999,7 @@ function installReadApi(app, options) {
|
|
|
3976
3999
|
message: "The read API only reads.",
|
|
3977
4000
|
fix: "Use GET."
|
|
3978
4001
|
}, 405, { allow: "GET, HEAD" });
|
|
3979
|
-
if (c.req.path === "/v1/openapi.json") return next();
|
|
4002
|
+
if (c.req.path === "/v1/openapi.json" || INDEX_PATHS.has(c.req.path)) return next();
|
|
3980
4003
|
c.res = await options.protect(c, async (principal) => {
|
|
3981
4004
|
if (!principal.scopes.includes("content:read")) return insufficientScope(["content:read"], options.metadataUrl(c));
|
|
3982
4005
|
c.set("site", principal.site);
|
|
@@ -4000,6 +4023,13 @@ function installReadApi(app, options) {
|
|
|
4000
4023
|
return c.res;
|
|
4001
4024
|
});
|
|
4002
4025
|
app.get("/v1/openapi.json", (c) => c.json(openApiDocument()));
|
|
4026
|
+
for (const path of INDEX_PATHS) app.get(path, (c) => {
|
|
4027
|
+
const origin = new URL(c.env.HOLLWARE_PUBLIC_URL).origin;
|
|
4028
|
+
return c.json({
|
|
4029
|
+
openapi: `${origin}/v1/openapi.json`,
|
|
4030
|
+
site: `${origin}/v1/site`
|
|
4031
|
+
});
|
|
4032
|
+
});
|
|
4003
4033
|
app.get("/v1/site", readRoute(async (c) => answer(c, await options.read(c.env).describeSite(c.get("site")))));
|
|
4004
4034
|
app.get("/v1/search", readRoute(async (c) => {
|
|
4005
4035
|
const query = c.req.query();
|
|
@@ -6825,6 +6855,19 @@ async function currentOf(context) {
|
|
|
6825
6855
|
definition: siteDefinitionData.parse(stored.definition)
|
|
6826
6856
|
};
|
|
6827
6857
|
}
|
|
6858
|
+
/**
|
|
6859
|
+
* A change sent at a version that is not the current one. It says which version is, and that the
|
|
6860
|
+
* change was not applied: whoever changed the structure since (often the caller's own earlier
|
|
6861
|
+
* update_site) made the definition that was read stale.
|
|
6862
|
+
*/
|
|
6863
|
+
function conflict(sent, current) {
|
|
6864
|
+
return {
|
|
6865
|
+
outcome: "conflict",
|
|
6866
|
+
currentVersion: current,
|
|
6867
|
+
message: `The structure is at version ${current}, not ${sent}: it changed since you read it, so nothing was applied.`,
|
|
6868
|
+
fix: `Call get_structure again, apply your change to the definition it returns (version ${current}), and send it with version ${current}. An earlier update_site or set_collection of yours also moves the version.`
|
|
6869
|
+
};
|
|
6870
|
+
}
|
|
6828
6871
|
/** Replaces the definition of the site of the call with `next`, as update_site answers. */
|
|
6829
6872
|
async function change(context, version, next, confirm) {
|
|
6830
6873
|
const structure = structureOf(context);
|
|
@@ -6834,10 +6877,7 @@ async function change(context, version, next, confirm) {
|
|
|
6834
6877
|
problems
|
|
6835
6878
|
};
|
|
6836
6879
|
const current = await currentOf(context);
|
|
6837
|
-
if (current.version !== version) return
|
|
6838
|
-
outcome: "conflict",
|
|
6839
|
-
currentVersion: current.version
|
|
6840
|
-
};
|
|
6880
|
+
if (current.version !== version) return conflict(version, current.version);
|
|
6841
6881
|
if (canonicalJson(current.definition) === canonicalJson(next)) return {
|
|
6842
6882
|
outcome: "unchanged",
|
|
6843
6883
|
version
|
|
@@ -6859,10 +6899,7 @@ async function change(context, version, next, confirm) {
|
|
|
6859
6899
|
definition: next,
|
|
6860
6900
|
actor: actor(context),
|
|
6861
6901
|
event
|
|
6862
|
-
}) === "conflict") return
|
|
6863
|
-
outcome: "conflict",
|
|
6864
|
-
currentVersion: (await currentOf(context)).version
|
|
6865
|
-
};
|
|
6902
|
+
}) === "conflict") return conflict(version, (await currentOf(context)).version);
|
|
6866
6903
|
context.notify?.([event.id]);
|
|
6867
6904
|
await structure.changed();
|
|
6868
6905
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hollware/server",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.1",
|
|
4
4
|
"description": "The Hollware Worker: operation handlers, MCP, RPC, HTTP, auth and D1 migrations.",
|
|
5
5
|
"homepage": "https://hollware.com",
|
|
6
6
|
"bugs": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@cloudflare/workers-oauth-provider": "1.2.3",
|
|
33
|
-
"@hollware/core": "1.0.0-rc.
|
|
33
|
+
"@hollware/core": "1.0.0-rc.1",
|
|
34
34
|
"@modelcontextprotocol/server": "2.3.1",
|
|
35
35
|
"@simplewebauthn/server": "14.0.3",
|
|
36
36
|
"drizzle-orm": "0.45.4",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@cloudflare/vitest-plugin": "1.4.3",
|
|
41
|
-
"@hollware/agents": "1.0.0-rc.
|
|
41
|
+
"@hollware/agents": "1.0.0-rc.1",
|
|
42
42
|
"@modelcontextprotocol/client": "2.3.1",
|
|
43
43
|
"@types/node": "24.19.2",
|
|
44
44
|
"drizzle-kit": "0.31.11",
|